CIL中的nop有什么意义

所以我在C#中编写了以下代码。

class Test { int a; System.IO.StreamReader reader; public Test() { a = 5; reader = new System.IO.StreamReader(String.Empty); } } 

IL中类的构造函数看起来像这样

 .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 33 (0x21) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: nop IL_0008: ldarg.0 IL_0009: ldc.i4.5 IL_000a: stfld int32 Test2.Test::a IL_000f: ldarg.0 IL_0010: ldsfld string [mscorlib]System.String::Empty IL_0015: newobj instance void [mscorlib]System.IO.StreamReader::.ctor(string) IL_001a: stfld class [mscorlib]System.IO.StreamReader Test2.Test::reader IL_001f: nop IL_0020: ret } // end of method Test::.ctor 

有3个nop命令。 (据我所知,这表示无操作)。 这些命令的需要是什么。 我的意思是如果根本没有命令而不是nop会有什么区别

它们在为程序写入.pdb文件时由C#编译器使用。 其中包含调试信息,其中包含代码的文件+行号信息。 调试器使用它来查找需要注入INT 3指令的机器代码,以便在设置断点时让程序停止执行。 抖动为MSIL中的每个Opcodes.Nop发出NOP机器代码指令。

public Test()上设置断点时使用第一个nop。 请注意,它是基础构造函数调用之后注入的以便变量在Auto / Locals / Watch调试窗口中变为有效。

在第一个{花括号上设置断点时使用第二个nop。 该行根本不生成任何代码,因此非常需要伪造的MSIL指令。

第三个nop的相同故事,为最后一个大括号生成。 当您在该断点上设置断点时,您可以检查方法返回值(如果有)。 在Debug + Windows + Registers窗口中间接可见。 在VS2013中得到改进。

因此,这只是帮助调试程序,它们使断点可以预测。 在构建程序的Release配置时, 不会生成这些NOP。 C#项目具有Debug和Release配置的一个重要原因。 您仍然可以调试Release版本,但这是一个相当混乱的体验,让您怀疑自己的理智:)

它们用于在调试模式下构建时支持断点,并且对程序集的执行没有任何影响。