C# compiler doesn’t always emits a virtual call (callvirt)

MSIL provides two ways to call a method in IL level with opcodes call and callvirt respectively.

In a nutshell call opcode calls the method with early binding where as callvirt makes the late bound call(method will be chosen at runtime rather than compile time).

Apart from the early/late binding difference, callvirt also checks if the instance in which the method is being called is null, if so –It will throw NullReferenceException.

If you do a web search you could easily reach some blog posts, forums which says that C# compiler  always emits callvirt for even non virtual methods. Me too tend to believe that was true; it turns out that it is not.

You have been mislead

Hell no! You have been mislead. Although C# compiler usually emits callvirt for non virtual methods, it does emits call opcode when it can prove that the object reference is not null.

Technically there is no reason for the compiler to emit callvirt for non virtual methods or sealed methods as we already know that late binding isn’t going to make any difference because you can’t override a sealed or non virtual method. But compiler emits callvirt just for the sake of getting free null checks by the runtime.

Why does that matters? Why can’t we allow instance methods to be invoked with null instance? Fail Fast is better than allowing you to call a instance method with null reference and surprising you at some point with NullReferenceException where this reference is accessed.

IL code for the above snippet(only Main method) is as follows

Above snippet shows a case where C# compiler emits a call opcode for instance method.

Yeah C# compiler is being smart[1] here by emitting call opcode because it can prove that the instance can never be null. So that it couldn’t waste the cpu cycles in testing for null which we already know it isn’t.

As soon as I discovered this myself, googling little bit turns out that I’m not the first guy to discover it.

1:C# compiler is smart, but not smart enough.