在VS中,有两个编译器开关会影响生成的代码的优化:
一个是在项目属性->生成->优化代码选项,如果没有选中该选项,则生成的IL代码是没有经过优化的,在IL文件中会包含很多NOP指令,这些指令是空操作指令,作用是方便设置断点,在流程控制指令后边都会添加NOP指令,对于下面的简单代码:
static class Program { /// <summary> /// 应用程序的主入口点。 /// </summary> [STAThread] static void Main() { if (true) { Console.WriteLine("123"); } } }
如果没有经过优化,生成的IL代码是:
.method private hidebysig static void Main() cil managed { .entrypoint .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) // 代码大小 17 (0x11) .maxstack 1 .locals init ([0] bool CS$4$0000) IL_0000: nop IL_0001: ldc.i4.0 IL_0002: stloc.0 IL_0003: nop IL_0004: ldstr "123" IL_0009: call void [mscorlib]System.Console::WriteLine(string) IL_000e: nop IL_000f: nop IL_0010: ret } // end of method Program::Main
如果选中了优化代码选项,则不会添加NOP指令,则调试的时候没法在流程控制指令(while、if、try、catch等等)上添加端点,对单步调试带来很大困难。但是经过优化的代码是文件变得更小。经过优化后,生成的代码如下:
.method private hidebysig static void Main() cil managed { .entrypoint .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) // 代码大小 11 (0xb) .maxstack 8 IL_0000: ldstr "123" IL_0005: call void [mscorlib]System.Console::WriteLine(string) IL_000a: ret } // end of method Program::Main
在VS中默认新建一个工程,debug模式是不会进行优化代码的而release模式默认是进行优化代码的,所以我们在写代码的过程中最好使用debug模式,在发布时最好使用release模式。
另外一个属性是在生成->高级里边的调试信息,对于C#而言,该选项一共有三个,none、pdbonly和full。None选项是最少使用的一个,在vs的默认设置中是不需要设置这个的,因为none和pdbonly就性能来说没有任何的不同,并且pdbonly还有独有的优势,这个优势下面马上进行说明。之所以会出现none选项是因为在.NET 1.0的时候他们确实是有区别的。具体信息读者可以查相关文档。
接下来要说的是pdbonly和full,默认VS在debug模式下会设置为full,在release下设置为pdbonly。如果设置成pdbonly,build的时候会生成pdb文件。这个pdb文件中包含IL指令到源代码之间的映射,这个映射可以使得程序在release下运行并且能够进行调试(不是跟进程,而是直接在vs启动),如果我们通过调试-->窗口-->模块观察,可以看到这个时候程序是去加载符号文件的,所以才能够获取断点。
但是pdbonly不能跟进程进行调试,这也是pdbonly和full的主要区别,在生成程序集的时候,有一个属性debuggableAttribute,这个属性标识符号文件会不会连接调试器,full下是true,pdbonly是false,所以我们在通过调试-->窗口-->模块观察,pbdonly程序通过跟进程调试,这个时候程序是跳过符号文件的。