Question

So I wrote the following code in C#.

class Test
{
    int a;
    System.IO.StreamReader reader;

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

And the constructor of the class in IL looks like this

.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

There are 3 nop commands. (Which as I know stands for no operation). What is the need of those commands. I mean what would be the difference if there was no command at all instead of nop

Was it helpful?

Solution

They are used by the C# compiler when it writes the .pdb file for your program. Which contains debugging info, which includes file+line number info for your code. The debugger uses this to find the machine code where it needs to inject an INT 3 instruction to get the program to stop executing when you set a breakpoint. The jitter emits a NOP machine code instruction for each Opcodes.Nop in the MSIL.

The first nop is used when you set a breakpoint on public Test(). Note that it is injected after the base constructor call so that the this variable becomes valid in the Auto/Locals/Watch debugging windows.

The second nop is used when you set a breakpoint on the first { curly brace. That line generates no code at all so there's a hard need for a fake MSIL instruction.

Same story for the third nop, generated for the last } curly brace. When you set a breakpoint on that one then you can inspect the method return value (if any). Visible indirectly in the Debug + Windows + Registers window. Improved in VS2013.

So this just aids in debugging your program, they make breakpoints act predictably. Those NOPs are not generated when you build the Release configuration of your program. One big reason why a C# project has a Debug and a Release configuration. You can still debug a Release build, it is however a pretty confounding experience that makes you doubt your sanity :)

OTHER TIPS

They are used to support breakpoints when building in debug mode and will make no difference to the execution of your assembly.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top