首页 > Python基础教程 >
-
C#教程之用csc.exe和记事本写一个C#应用程序
csc.exe是C#的命令行编译器(CSharpCompiler),可以编译C#源程序成可执行程序。它与Visual Studio等IDE(Integrated Development Environment,集成开发环境)的区别是,csc.exe只是将用C#语言编写的源程序文件编译成.exe、.dll等文件,它只是一个编译器,而IDE提供丰富的调试、运行功能,提供很多view视图以及解决方案管理器等文件组织功能。
csc.exe包含在.NET Framework SDK(Software Development Kit)中,除了csc.exe外,SDK还包括其他语言的编译器,如vbc.exe(vb语言的编译器)以及其他一些可能会用到的工具、文档等。.NET Framework SDK在微软官网可以下载,但现在好像包含在了.NET Framework Developer Pack(开发者包)里。https://www.microsoft.com/net/download。在这里可以下载.NET Framework和.NET Core的最新版本及之前的版本。
如果不想那么麻烦,可以直接安装Visual Studio,.NET Framework SDK(或Developer Pack)是自动随着IDE的安装而安装的。可以在这里下载免费的Visual Studio 2017社区版:https://visualstudio.microsoft.com/zh-hans/downloads/,也可以在这个页面下方选择安装之前的旧版本。
安装好SDK后,在磁盘中会多出很多.NET开发工具,其中一些工具需要在命令提示符中打开(Command,CMD)。但这些工具分散在各个文件夹下,需要为它们配置PATH环境变量,这样很麻烦。在安装好Visual Studio后,磁盘中会多出一个类似于CMD的命令提示符,叫作Developer Command Prompt for VS + VS的版本号。Developer Command Prompt预先配置了每一个.NET开发工具,不需要为它们注册PATH路径,就可以在这里直接使用了。
比如这里,输入csc -?,窗口中显示了C#编译器的命令行参数列表;同样地,输入vbc -?,窗口显示VB编译器的命令行参数列表。其中,有些参数在我们用csc.exe编译C#源文件时会用到。
一、编辑源程序
在C盘新建一个文件夹test,在文件夹里创建一个记事本文件HelloWorld.txt。用记事本打开编辑这个文件,输入C#代码,注意输入空格以缩进。之后将HelloWorld.txt后缀名改为HelloWorld.cs。*.cs是C#代码文件的后缀名。
二、打开Developer Command Prompt,启动csc.exe编译源程序
更换路径到源程序所在文件夹下。
输入csc HelloWorld.cs,启动编译。
编译成功,在test文件夹下,可以看到HelloWorld.cs已经被编译成一个可执行程序HelloWorld.exe。
三、C#编译器(csc.exe)的输出选项
前面已经实现了用记事本编辑C#源程序并用csc.exe编译,最后在文件夹下可以成功打开编译而来的可执行程序。现在,考虑编译过程中的一些细节。
源程序叫作HelloWorld.cs,编译后的程序叫作HelloWorld.exe。看上去没什么问题,其实编译后的程序的名字和类型是可以通过C#编译器的输出选项(参数)指定的。
①/out 规定编译后文件名字,如果不进行设置,编译后文件名与源程序名字相同 ②/target:exe 编译源程序成可执行的控制台程序,缺省选项 ③/target:library 编译源程序成*.dll程序集 ④/target:winexe 编译源程序成窗体程序 (其中/target可缩写成/t)
所以之前在编译时,我们输入:csc HelloWorld.cs其实等价于输入:csc /target:exe /out:HelloWorld.exeHelloWorld.cs,只是中间两个选项都省略掉了。命令行选项和源文件的顺序是任意的,可以命令行选项在前,源文件在后。也可以源文件在前,命令行选项在后,甚至可以这样写:csc /target:exe HelloWorld.cs /out:HelloWorld.exe,这些都是可以的。
打开Developer Command Prompt,进行其他选项的测试。
此外,命令行输出选项中的"/"符号还可以换成"-"符号。
四、源程序中引用了外部程序集
修改源文件。using System.Windows.Forms引入了一个命名空间,以使用其中的MessageBox类的静态方法Show(),弹出一个对话框显示相关内容。System.Windows.Forms命名空间存在于System.Windows.Forms.dll程序集中(命名空间与程序集名称相同)。源文件中使用了外部程序集的内容,csc.exe在编译时需要通过/reference命令行选项指定引用的外部程序集(/reference可缩写为/r)。
下面进行编译,省略/target和/out选项。
可能有读者在这里并没有/reference:System.Windows.Forms,而是直接csc HelloWorld.cs,也可以成功编译并运行,在后文会对这个问题进行解答。(C#默认响应文件csc.rsp的概念)
这里还有一个问题,在源文件中,使用System.Windows.Forms命名空间中的MessageBox类,需要引入这个命名空间所在的程序集System.Windows.Forms.dll,那使用System命名空间中的Console类,为什么不引入System命名空间所在的程序集System.dll?
这就涉及到了“外部程序集”中“外部”的概念。在Visual Studio中,引入System.Windows.Forms.dll程序集后,查看其包含的内容。可以看到Show()方法是MessageBox类的静态方法,MessageBox类是System.Windows.Forms命名空间中的成员,而这个命名空间又是System.Windows.Forms.dll程序集中的逻辑概念。下图中,左侧两个小黑方块中间用一杠连起来的那个符号就是程序集的符号,花括号的那个符号是命名空间的意思。
System.Windows.Forms.dll是外部程序集,那什么又叫作“不是外部程序集”呢?查看System.dll程序集中的System命名空间的成员,发现并没有我们使用的Console类。
这是因为,我们使用的Console.WriteLine()中的Console类所在的命名空间System,不是包含在System.dll中的!我们查看mscorlib这个程序集。
Console类原来是mscorlib.dll程序集中System命名空间的成员。mscorlib(MicroSoft CORe LIBrary),相对于System.Windows.Forms等一些外部程序集来说,是.NET平台中核心的程序集,可以把它们理解成“内部的”程序集。所以回到之前的那个问题,使用mscorlib.dll程序集中命名空间中的成员,只要在源文件中使用using关键字引入所用成员所在的命名空间即可(也可使用成员的完全限定名),而不用在使用csc.exe编译时通过/reference命令行选项额外引入mscorlib.dll这个程序集。
其实,在Visual Studio IDE里看这个问题,也是相通的。在我们新建了一个控制台应用程序后,在解决方案管理器中References下会自动引入一些外部程序集。(mscorlib不显示在其中,因为它不需要引入!)当需要使用MessageBox等一些没有自动被引入的外部程序集中的成员时,就需要我们手动右键References添加所用的程序集。而mscorlib.dll是在创建应用程序时就已经添加进来的程序集,不用额外引入。在IDE中是这样,在命令行提示符中使用csc.exe编译时也是这样。
在Visual Studio中可以看到程序集在磁盘中的路径。
在Visual Studio中创建应用程序时会选择.NET Framework的版本,因为例子中这个程序选择的是.NET Framework 4.6.1,所以它引用的程序集就是磁盘中.NET Framework 4.6.1中的程序集。
进入这个文件夹,可以看到其中包含的程序集。我们看到mscorlib.dll这个程序集的容量很大,也反映出这个程序集的重要性。
如果csc.exe需要引入多个外部程序集,在/reference:程序集1;程序集2 中用英文分号";"分隔即可。
五、编译多个源文件
现在我们已经可以使用csc.exe和记事本构建一个可以引用外部程序集的控制台应用程序了,但是我们所有的代码都是写在一个C#源文件中的。我们也可以将代码写在多个文件中,使用csc.exe对多个源文件进行编译。
在test文件夹下增加MyMessageBox.txt文件,之后要将后缀名改为.cs(下同)。
修改HelloWorld.txt文件。
打开Developer Command Prompt,编译这两个文件,注意依然要使用/reference引入使用的外部程序集。编译的多个文件之间用空格隔开。
执行这个程序。
此外,在使用csc.exe编译多个文件时,还可以使用通配符"*"表示编译当前目录下的所有指定后缀名文件。如:csc *.cs表示编译当前目录下所有.cs文件。
六、使用C#响应文件
至此,我们已经可以使用csc.exe编译多个源文件的C#应用程序了。在编译过程中,需要设置一些命令行选项以达到某种效果。当程序规模大到一定程度,就需要输入很多的命令行选项,容易出错且录入工作量大。为了应对这一问题,C#有响应文件(response file,后缀名为rsp)的概念。C#响应文件包含编译一个或多个C#源文件时需要指定的命令行选项。
编辑HelloWorld.rsp文件,将编译源文件时需要用到的引入外部程序集、设置输出程序的类型及名字等命令行选项写在这个文件里,并将这个响应文件和源程序文件放在同一目录下。csc.exe编译时,输入csc@HelloWorld.rsp就可以按照设置的命令行选项编译这个C#源程序了。
程序可以正常运行。
这里有几点需要注意的地方:①写在后面的命令行选项或响应文件的内容会覆盖掉前面的命令行选项或响应文件的内容(以后规定的为准!) ②/reference具有累加性,最终程序引用的外部程序集为各个地方(命令行选项和响应文件)规定的程序集的并集
七、关于C#默认的响应文件csc.rsp
回到之前提到的那个问题,为什么即使没有使用/reference引入需要的外部程序集,程序也是可以成功编译并执行的?
C#编译器(csc.exe)有一个与之关联的默认响应文件(csc.rsp),csc.rsp与csc.exe在同一目录下(我没有找到..)。在编译C#源程序时,无论有没有自己编写的响应文件,这个默认的响应文件csc.rsp都会执行。而csc.rsp已经写好了对很多核心程序集的引用(用/reference或/r命令行选项)。所以即使我们没有使用/reference引入外部程序集,csc.rsp也帮我们引入好了。
如果不想在编译时自动执行csc.rsp,可以指定/noconfig选项。
如果我们在编译时指定了/noconfig选项且没有使用/reference引入源文件中需要的外部程序集,那么编译将报错。
如果程序引用了在源文件中没有用到的外部程序集(比如csc.rsp帮我们自动引入的),它们将会被编译器忽略,所以不会影响程序的质量。