代码改变世界

C#编译基础知识(一)

2014-06-21 11:39  HaiZL  阅读(347)  评论(0)    收藏  举报

1 准备工作


csc.exe是.net Framework提供的一个编译工具,对应的语言为C#,vb语言对应的编译器是vbc.exe。为了使用上的方便,我们需要把这个文件所在的路径放到环境变量path中,方便后面直接调用,这个文件位于C:\Windows\Microsoft.NET\Framework\v4.0.30319下,不同的版本最后一个文件夹会有差异。

 

2 一个简单的例子


 源码如下,一个再简单不过的控制台命令程序。

using System;
///一个简单的demo
public class a{
  ///应用程序的主入口Main方法
public static void Main(){ Console.WriteLine("Hi"); } }

编译命令很简单csc d:\a.cs,该源文件位于d盘根目录下。

  该源码编译完成后将生成一个可执行的exe文件,就存在于C:\Users\liuzh下,执行之后的结果也是显而易见的

当然,生成的文件路径和文件名是可以随意修改的,显示的指定/out:开关即可,通过执行命令csc -?可以看到详细的描述

修改后的命令如下,执行之后生产的a.exe就会出现在d:\下面

    csc /out:d:\a.exe d:\a.cs

有时候我们需要生成的可能不是exe,只是一个dll,那么需要显示的指定/target:开关,可以缩写成/t:,该开关的说明如下

  

  那么修改后的命令如下

    csc /out:d:\a.dll /target:library d:\a.cs

  对于其他类型开关的一个说明

  /t:module 生成的是一个托管模块,可用于将来合并到其他应用程序集

  /t:winmdobj 将生成一个windows运行时中间文件,之所以叫做中间文件,是因为它必须被windows运行时元数据导出工具导出后才能使用。

  /t:appcontainerexe 将生成一个windows应用商店里面的应用

  /t:winexe将生成一个winform应用程序

  下面给出一个/t:winexe的例子

     源代码为

using System.Windows.Forms;
using System;
public class f{
    public static void Main(){
        Form f=new Form();
        f.Text ="New Form";
        f.Show();
        Application.Run();        
    }
}

编译命令为

  csc /out:d:\f.exe /t:winexe d:\f.cs

最后运行的效果如下

   

如果我们想得到类的成员及注释信息,得加上/doc:开关

编译命令为

  csc /out:d:\a.exe /doc:d:\a.xml /t:library d:\a.cs

最后生成的d:\a.xml文件是这个样子

  

 3 类之间相互引用时的编译及响应文件


 假如现在类a要引用类b的方法,但a和b都在一个程序集时,编译时则就需要把a.cs和b.cs都包含进来,就像这个样子

csc d:\a.cs d:\b.cs

 如过b是一个应用程序集,如b.dll,类a需要引用b中的一个方法,那么编译时则需要加上/reference:开关,简写/r:,编译时的命令如下

  csc d:\a.cs /r:d:\b.dll

 当使用/reference:开关时,引用的应用程序集可以是一个特定的完整路径,如果不指定路径时(csc d:\a.cs /r:b.dll)编译器会从如下位置逐一进行寻找

  1 工作目录 C:\Users\Liuzh

  2 csc.exe本身所在的目录

  3 使用/lib:开关指定的任何目录。

  4 环境变量lib指定的任何目录

 是时候来说说响应文件了,响应文件是一个文本文件,其中包含了编译器命令开关,执行csc.exe时,编译器会打开响应文件,并使用其中的所有开关,感觉就像是这些开关直接在命令行上传递给csc.exe一样,要在命令行中使用响应文件,在文件名前加上@。对于命令csc /out:d:\a.exe /doc:d:\a.xml /t:library d:\a.cs,假如我们的响应文件MyProgram.rsp内容为/out:d:\a.exe /doc:d:\a.xml /t:library,那么最后的编译命令为

  csc @d:\MyProgram.rsp d:\a.cs

 C#编译器允许同时制定多个响应文件,除了在命令行上显式制定文件,csc.exe在运行时还会自动寻找在csc.exe所在目录的CSC.RSP文件。

 在安装.net framework时,默认就会在%SystemRoot%\Microsoft.NET\Framework\vX.X.X目录中安装一个CSC.RSP文件,4.0环境下的这个文件包含如下内容(部分截图)

 

4 生成调试信息及代码优化


 示例代码如下

using System;
public class c
{
    public static void Main(){
     Console.ReadLine();
if ("Aa"!="AA"){ Console.WriteLine("Really Different!"); } } }

 控制调试信息和优化信息的两个开关分别是/optimize 和 /debug,csc帮助中的描述为

如果要生成一个可以用于调试的dll或exe,则需要使用命令 csc d:\c.cs /debug:full,运行后将会生成d:\c.exe和d:\c.pdb,使用vs可以直接附加进程调试

使用/debug:pdbonly时,生成的内容则不能进行调试,使用 /debug:full 时,编译器会发出 DebuggableAttribute,用于通知 JIT 编译器有可用的调试信息,比对它们生成的元数据的差异可以看出这一点

当使用/optimize+生成时,对比/debug生成的IL,可以发现很多NOP指令都消失了,代码行数也少了很多(如图左侧为debug生成,右侧为optimize)

这两个开关生成的代码其实还会影响到最后的JIT编译,他们组合起来时对IL和JIT编译的影响如下   

编译器开关设置 C#IL代码质量 JIT本地代码质量
/optimize- /debug-(默认) 未优化 有优化
/optimize- /debug(+/full/pdbonly) 未优化 未优化
/optimize+ /debug(-/+/full/pdbonly) 有优化 有优化

 

 5 总结


 

本文走马观花似的对C#的csc命令做了一个基本的介绍,这些内容在csc的帮助文档里面都有一个比较全面的介绍,了解这些命令有助于我们平常排查编译过程中遇到的各种错误,能为深入了解CLR执行模型打下良好的基础。