I am curious to know why this is happening. Please read the code example below and the corresponding IL that was emitted in comments below each section:
using System;
class Program
{
static void Main()
{
Object o = new Object();
o.GetType();
// L_0001: newobj instance void [mscorlib]System.Object::.ctor()
// L_0006: stloc.0
// L_0007: ldloc.0
// L_0008: callvirt instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
new Object().GetType();
// L_000e: newobj instance void [mscorlib]System.Object::.ctor()
// L_0013: call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
}
}
Why did the compiler emit a callvirt
for the first section but a call
for the second section? Is there any reason that the compiler would ever emit a callvirt
instruction for a non-virtual method? And if there are cases in which the compiler will emit a callvirt
for a non-virtual method does this create problems for type-safety?
Just playing safe.
Technically C# compiler doesn't always use callvirt
For static methods & methods defined on value types, it uses call
. The majority is provided via the callvirt
IL instruction.
The difference that swung the vote between the two is the fact that call
assumes the "object being used to make the call" is not null. callvirt
on the other hand checks for not null and throws a NullReferenceException if required.
call
is used for them - better performance.callvirt
so the JIT compiler verifies that the object being used to make the call is not null. Even for non-virtual instance methods.. they valued safety over performance.See Also: Jeff Richter does a better job at this - in his 'Designing Types' chapter in CLR via C# 2nd Ed