13

所以我用 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

4

2 回答 2

19

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

第一个 nop 用于在 上设置断点public Test()。请注意,它是在基本构造函数调用之后注入的,因此this变量在 Auto/Locals/Watch 调试窗口中变得有效。

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

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

所以这只是有助于调试你的程序,它们使断点的行为可预测。当您构建程序的发布配置时,不会生成这些 NOP 。C# 项目具有 Debug 和 Release 配置的一个重要原因。您仍然可以调试 Release 版本,但是这是一种非常令人困惑的体验,让您怀疑自己的理智 :)

于 2013-10-06T13:41:22.663 回答
3

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

于 2013-10-06T07:30:04.580 回答