call and callvirt
callvirt are the two instructions emitted by the IL to call the functions in .NET. Being a developer affinity to both of these instructions is not a must as .NET framework takes care of it. But we should be aware of what is happening inside the code.
Or basically I would like to discuss here how the virtual methods, properties are called at run time using the
callvirt instruction. If you have ever have got a chance of looking at the IL using ILDASM.exe for the instruction emitted, we will find that even for the non-virtual method, the
callvirt instruction is emitted. I will discuss both of these instructions here. Please take the following code snippet as reference.
public class Animal
public string GetAnimalType()
public static string GetQualities()
public virtual string GetFeatures()
public override string ToString()
return "generic animal";
static void Main(string args)
Animal person = new Animal();
When compiler executes the code for the
Animal class, it emits three entries in the resulting assemblies’ method definition table indicating if the function is virtual, instance or static method. And when any of these functions is called from code, the compiler examines the same method definition’s flag to judge how to emit the proper IL code so that the call is made correctly.
As we can see in the above figure, the CLR has emitted two types of calls which I have explained below.
call – Explanation
This IL instruction can be used to call static, instance and virtual methods. The major thing which we should keep in mind is that the
call IL instruction assumes that the instance variable which we are using to call the method is not
null. In case of
static methods, we must specify the type in which the method is being called and in case of instance or virtual method, the instance variable should be used. So the type of the variable can be used to refer to the function, if the function is not present in that particular type, the base classes are scanned for the presence of the function. Compilers also emit the
call IL instruction when calling methods defined by values type as values type are sealed.
callvirt – Explanation
callvirt IL instruction is used to call the virtual and instance methods, not the
static ones. In this case also, we need the type variable that refers to the object which contains the functions.
callvirt is basically used to call the methods associated with the reference contained in the variable type at run time. When
callvirt is being used to call the nonvirtual method of the type, the type of the variable is used to refer to the exact function which the CLR should call. But when
callvirt is used to call a virtual method of a type,
callvirt takes into account the type of the object on which the method is being called in order to provide us with the polymorphic behaviour that we expect from such cases. And while executing this code, the JIT compiler generates the code which checks for the nullability of the variable which the
call IL doesn’t do and if it is
NullReferenceException is being thrown by CLR.
Now we can discuss the above code and the IL which it generates.
As we can see the call to the
static function of the
Animal class, a
call instruction is generated which is the expected behaviour. Just after that, we can see that to call the
GetFeatures() virtual function of the class
callvirt instruction is generated which is also at par with what we have discussed earlier. But if we would not have been aware of the working of the
callvirt’s basics, then the third call would have been a surprise for us. But as we know, the compiler generates
callvirt IL instruction to call the non-virtual function, that we can see in the IL code. Even to call
GetAnimalType() non-virtual function,
callvirt instruction is generated which is used to call this function nonvirtually.
To support our belief that
callvirt calls the methods at runtime, I will demo a small code snippet. I have defined a function as shown below:
public static void GetString(object var)
which I will call from my
Main() function as shown below:
As we can see from the definition of the
ToString() function has been overridden. The IL code for the
GetString(object var) is as follows:
Here, in this IL, we can see that
callvirt has been used to call the
var.ToSting() function. But at index 1, we can notice that an argument is loaded on the stack. This argument is nothing but the
var parameter of the function. When
callvirt is used to call the
ToString() method, it first checks the
null reference and then the correct class for which the
ToString() method should be called using this argument only.
Last but not the least; I would like to discuss one more scenario where virtual function is called using the
call IL instruction. This is definitely ambiguous to whatever we talked till now in the article. If I would implement the
ToString() function defined in the
Animal class as below:
public override string ToString()
And the IL generated for the above code is as shown below:
Why this case, whenever the compiler sees the call to the case function using
base keyword, it emits the
call IL instruction to ensure the
ToString method in the base type is called nonvirtually. This is a must as if the
ToString would have called virtually in that case the Animals
ToString would have been called again and again resulting in the thread’s stack overflow.