I am trying to understand how the CLR implements reference types and polymorphism. I have referred to Don Box’s Essential .Net Vol 1 which is a great help to calrify most of the stuff. But I am stuck/confused by the following issue when I tried to play around with some IL code to understand better.
I will try to explain the problem as best as I can.
Consider the following code
class Base
{
public void m()
{
Console.WriteLine("Base.m");
}
}
class Derived : Base
{
public void m()
{
Console.WriteLine("Derived.m");
}
}
Now consider a simple console application with IL of the main method shown below.
I tweaked the IL created by compiler manually to understand and assembled again with ILAsm.exe
.class private auto ansi beforefieldinit Console1.Program
extends [mscorlib]System.Object
{
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 44 (0x2c)
.maxstack 1
.locals init ([0] class Console1.Base d)
nop
newobj instance void Console1.Base::.ctor()
stloc.0
ldloc.0
callvirt instance void Console1.Derived::m()
nop
call string [mscorlib]System.Console::ReadLine()
pop
ret
} // end of method Program::Main
} // end of class Console1.Program
I was expecting this code NOT to run as the object reference is pointing to an object of Base and there is no way the method table of a base object will have an entry for the method m() defined in Derived class.
But magically this code executes the Derived.m()!!
So, there are two questions I don’t understand in the above code:
-
What is the significance of the Type specified in the below IL code? I have tried to experiment by changing this to different types (e.g System.Exception!!) and no errors are reported. Why??
.locals init ([0] class Console1.Base d)
- How exactly does callvirt works? How did the call get routed to Derived.m()?
Thanks in advance!!
Regards,
Ajay
My guess is that the jitter realizes that
Derived.misn’t virtual and thus can never point anywhere else. So thecallvirtreduces to a null-check and a call instead of a call through the v-table.Try making
Derived.mvirtual. I bet it’ll throw then.The C# compiler emits
callvirtinstructions even when calling a non virtual methods if it can’t prove thatthis!=nullso it gets a null-check. And the jitter is intelligent enough in that case to replace the virtual call by a normal call with a fixed address(or even inline it).And you should check if you’re code is verifiable. I think it isn’t.