CLR internals

IL call vs. callvirt, part two

In the first part I wrote about what is call and callvirt, what the differences and why should I care. We saw that even non-virtual methods called via callvirt instruction because the nullcheck reason. But with this sense when this is not in use or when thiscan’t be null there is no need to check for *null.*for example: static class. And indeed for those cases the compiler emit call instruction.

Here is the cases that call instruction will emit:

static class methods
struct methods
new MyClass().SampleMethod();

The C# compiler / jit compiler know that it can’t be null so it generating a call instruction

What do you think happening here?

var c = new MyClass();
c.MyMethod();

Is this same as

new MyClass().MyMethod();

Check it yourself with reflectoror with Ildasm :-)

For who that want to know how all of those seem in ‘real life’ (it’s also answering on the above question), given the following code:

C#:

static void Main(string[] args)
{
var a = new myclass();
a.mymethod(args.Length);
}

IL: .method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 17 (0x11) .maxstack 2 .locals init ([0] class sealedvsnon.myclass a) IL_0000: newobj instance void sealedvsnon.myclass::.ctor() IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldarg.0 IL_0008: ldlen IL_0009: conv.i4 IL_000a: callvirt instance int32 sealedvsnon.myclass::mymethod(int32) IL_000f: pop IL_0010: ret } // end of method Program::Main

Disassembly:00CD0099 cmp dword ptr [ecx],ecx 00CD009B call dword ptr ds:[0BE380Ch]

As you can see callvirtand cmphas used.

And with the following code: C#:

static void Main(string[] args)
{
new myclass().mymethod(args.Length);
}

IL:.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 15 (0xf) .maxstack 8 IL_0000: newobj instance void sealedvsnon.myclass::.ctor() IL_0005: ldarg.0 IL_0006: ldlen IL_0007: conv.i4 IL_0008: call instance int32 sealedvsnon.myclass::mymethod(int32) IL_000d: pop IL_000e: ret } // end of method Program::Main

Disassembly:01220088 mov ecx,dword ptr [ebp-0Ch] 0122008B call dword ptr ds:[11C380Ch]

And here you can see that non callvirt or cmphas used.