4

Possible Duplicate:
Why is the C# compiler emitting a callvirt instruction for a GetType() method call?

I saw that when I call an instance method of a class the C# compiler emits the callvirt instruction to call that method, why is this so ?

Does it mean all instance methods are treated as virtual methods by the compiler, what's this mystery ?

Community
  • 1
  • 1
Embedd_0913
  • 16,125
  • 37
  • 97
  • 135
  • 1
    `callvirt` has the nice property of performing a `null` check; note that in (some) cases where the compiler *knows* that a non-virtual call has a non-null receiver, it will issue a `call` instruction. – dlev Jan 11 '13 at 17:24

3 Answers3

14

It is there to implement a promise made in the C# language specification. Which says that it is not legal to call an instance method of a class through a null reference. This may sound like an obvious feature, but it is not actually that common in OOP languages. In particular the C++/CLI compiler doesn't have it. And the CLI spec doesn't have it. An unmanaged language like C++ doesn't have it.

It even comes to a good end, sometimes, when the instance method doesn't use any non-static class members. Such a method should of course be static but that is not required or enforced.

The C# requirement is very nice, it makes a NullReferenceException much easier to diagnose. Since they are generated at the call site instead of inside the instance method., it clarifies that the object reference is null. Figuring out that the this reference is null inside a method is kinda difficult, especially since you can't see it. Further complicated by the address not actually being null, accessing a field of a class will generate an address that's offset from 0. Which in turn is unsafe if the object is humongous, more than 64 kilobytes. Accessing a field at the end of such a big object won't necessarily generate a processor exception, you'll just read random junk. Or corrupt memory if you write it.

So the C# team looked for a cheap way to implement the null test. And found one in the callvirt IL instruction. Which, unlike call, does promise an exception in the CLI specification. A very cheap test, it requires only a single machine code instruction. And doesn't require a branch, that's very expensive if the processor's branch prediction logic guesses wrong.

You now also know why String.Equals() contains this bit of mysterious code:

public override bool Equals(Object obj) {
    if (this == null)
        throw new NullReferenceException();
    // etc...
}
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
14

Hans and Mike's answers are correct. Just to add a little bit of extra information:

  • The callvirt instruction is explicitly documented as legal on a non-virtual method precisely so that you get the null check behaviour.

  • In those rare cases where the C# compiler proves that a non-virtual call cannot possibly have a null receiver, it falls back to call and saves the nanosecond it takes to do the null check. For example, if you have (new C()).InstanceMethod() then that should be generated as call, not callvirt because the compiler knows that the receiving expression is never null. (If the allocation fails then an exception is thrown, so the call will never be performed.)

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
2

Short answer: It's safer that way.

callvirt will first check if the object is null, and throw an exception if so.

You'll notice that calling a static method will still use call, as the object cannot be null.

Here's a little history.

Mike Christensen
  • 88,082
  • 50
  • 208
  • 326