Recently I was reading through a bunch of interesting blog posts. I was looking for information about the use of callvirt in C#. Callvirt can be used to call both virtual and non-virtual methods, and in fact is how all methods are called in C#. I don’t know how much of a performance decrease there is based on this, but I doubt it is much of one. I stumbled across this interesting post covering why C# uses callvirt from Eric Gunnerson’s blog.
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.
Now that certainly is odd behavior. I don’t know how I feel about it though. So basically one could have been able to call instance methods on null instances of an object as long as they didn’t access any members of the object itself. First I will say that methods with fewer dependencies are great.
So C# was of course adjusted to disallow this, but if it hadn’t then this could potentially work.
using System;
namespace NullInstanceMethod
{
class Program
{
static void Main(string[] args)
{
Enrick brendan = null;
Console.WriteLine(brendan.Awesome());
}
}
class Enrick
{
public string Awesome()
{
return "Awesome";
}
}
}
Yep, that method isn’t static. It’s an instance method, but you would have been able to call it.
So to see that in action why don’t we disassemble this code and make the change directly to the intermediate language.
First we need to get the IL using ildasm, so open up the VisualStudio 2008 Command Prompt and type this command.
ildasm PathToTheExecutable /out:new.il
This will create a file “new.il” in our VS Command Prompt working directory.
Now we can open this in our favorite editor and we will see a bunch of code. We are just looking at this main method though.
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 16 (0x10)
.maxstack 1
.locals init ([0] class NullInstanceMethod.Enrick brendan)
IL_0000: nop
IL_0001: ldnull
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: callvirt instance string NullInstanceMethod.Enrick::Awesome()
IL_0009: call void [mscorlib]System.Console::WriteLine(string)
IL_000e: nop
IL_000f: ret
} // end of method Program::Main
See IL_0004 there is our callvirt that makes the call to our instance method. If we change that to a regular call we should be good.
Now we just put the IL back into the assembly.
ilasm new.il
And we execute it and here is the result.
And there you have it. I just called an instance method on a null instance of a concrete class without getting a null reference exception? Strange? Yes. Wrong? I don’t know if it really is.
For your homework I would like you to consider what would happen if I were calling an interface’s method instead of a concrete class.
Comments