Why does C# always use callvirt?

This question came up on an internal C# alias, and I thought the answer would be of general interest. That's assuming that the answer is correct - it's been quite a while.

The .NET IL language provides both a call and callvirt instruction, with the callvirt being used to call virtual functions. But if you look through the code that C# generates, you will see that it generates a "callvirt" even in cases where there is no virtual function involved. Why does it do that?

I went back through the language design notes that I have, and they state quite clearly that we decided to use callvirt on 12/13/1999. Unfortunately, they don't capture our rationale for doing that, so I'm going to have to go from my memory.

We had gotten a report from somebody (likely one of the .NET groups using C# (thought it wasn't yet named C# at that time)) who had written code that called a method on a null pointer, but they didn’t get an exception because the method didn’t access any fields (ie “this” was null, but nothing in the method used it). That method then called another method which did use the this point and threw an exception, and a bit of head-scratching ensued. After they figured it out, they sent us a note about it.

We thought that being able to call a method on a null instance was a bit weird. Peter Golde did some testing to see what the perf impact was of always using callvirt, and it was small enough that we decided to make the change.

Comments

  • Anonymous
    July 02, 2008
    Interesting, kinda a fail-fast for null references. Do you recall why the thought was that being able to call a method on a reference that doesn't access "this" and not failing is a bad thing?  This happens in C++ and I've never heard that it's a issue.

  • Anonymous
    July 02, 2008
    Doesn't this also help in the case where a method in version 1 of a library is not virtual, but it is decided that it should be made virtual in version 2?  If the C# compiler built a program against version 1 it will always called the method non-virtually, even if the library was updated to version 2.  Going from non-virtual to virtual would have to be considered a breaking change in this case. (Or is it still a breaking change?) And the C# compiler doesn't use "callvirt" for struct methods.  (And obviously it doesn't for calls to static methods and calls through the "base" reference). Oh, and with the new Extension methods, you can simulate instance methods that accept a null "this" parameter. I've come across places where this can be useful, but I usually chicken out because it may end up being confusing in the long run.

  • Anonymous
    July 02, 2008
    Peter, We thought it was a bad idea because some smart guys got confused by the behavior, and there really isn't any reason to support the behavior. Matthew, I don't think that's really a benefit - it wouldn't be true in all other languages, so it would still be a breaking change.

  • Anonymous
    July 02, 2008
    Eric, Does this mean if we mark all our methods as virtual, there will be little performance impact?

  • Anonymous
    July 02, 2008
    Eric, Please read (The Old New Thing) => (http://blogs.msdn.com/oldnewthing/archive/2007/08/16/4407029.aspx). Isn't cmp [ecx], ecx solves the problem already?

  • Anonymous
    July 03, 2008
    I was wondering why c# supported static binding at all and didn't make everything virtual by default like jaba even in the language. I read Anders H's comments on that and he pointed out performance and version compatibility as two of the advantages of making non-virtual the default. What I am reading now sounds like those comments weren't correct. Am I missing something?

  • Anonymous
    July 03, 2008
    I get it now. So callvirt doesn't necessarily mean that methods that are not virtual will start behaving like virtual methods. Is that right?

  • Anonymous
    July 03, 2008
    The comment has been removed

  • Anonymous
    July 03, 2008
    The call opcode should test the this pointer when a instance method is called.. But i gess it would be a breaking change now. The msdn entry about the call opcode states that the this reference should not be null, but it's useless if it's not enforced. Perhaps the next version of the clr should containe à new opcode for that.

  • Anonymous
    July 04, 2008
    Eric, I understand the rationale, but you cannot imagine how many times (mostly dealing with web UI using a view engine and an IOC framework) I wished that calling a method on a null pointer wouldn't raise an exception in c# (or that at last there could be an easy syntax to teach runtime and compiler not to bother about that). I truly dread the appearence of the "System.NullReferenceException: Object reference not set to an instance of an object." text on my web pages :) Wouldn't be nice that instead of writing: object a = null; if (a != null) {    object b = a.ToString();    if (b != null)    {        object label = b.ToString();    } } we could write something like: label = CNRNR a.ToString().ToString(); where CNRNR means "Calls on Null Return Null and don't throw the [abovementioned] dreaded exception, Recursively", and I don't really care about the real syntax as long as I get that? Once we are there, if there would be a nice way to make it return string.empty if label is a string, that would be perfect ;)

  • Anonymous
    July 07, 2008
    I wrote a new post to address the comments here... http://blogs.msdn.com/controlpanel/blogs/posteditor.aspx?SelectedNavItem=Posts&sectionid=2963&postid=8702812

  • Anonymous
    July 07, 2008
    Well, you do support it and you don't support it. Extension methods support a null "this".  So, in that respect it's inconsistent.  Yes, extension methods compile to a direct static method call and can't be checked a runtime; but from a source-code view you've got two ways of working... I certainly don't think it's important enough to change at this point.

  • Anonymous
    July 07, 2008
    > Do you recall why the thought was that being able to call a method on a reference that doesn't access "this" and not failing is a bad thing?  This happens in C++ and I've never heard that it's a issue. Actually, it is undefined behavior to access any non-static member (including methods) via a null pointer in C++, and the compiler is free to insert a check. If any C++ code relies on this behavior, it's not conformant (there are a few such places in MFC source).

  • Anonymous
    July 08, 2008
    Actually, if I recall properly Delphi allows calls on null pointers and I've had many uses for them in being able to simulate static function inheritance. Not sure if delphi was building a VTable in those cases but it sure was a nice feature. I've come to places so far where trying to do similar things in C# was messy. Like if you have a class which needs a static property, and would like to force an implementation in derived instances, you could do abstract in Delphi. Then again, on our classes in C#, we got to remember to implement it all the time. Anyways, I wish it would had been there, or as an option to allow it.

  • Anonymous
    October 27, 2008
    Method calls using the C# ‘ base ’ keyword get compiled to an IL ‘ call ’ instruction, rather than the