Dot Net For All

CALL AND CALLVIRT INSTRUCTIONS IN .NET IL

call and callvirt Instructions

call and callvirt are the two instructions emitted by the IL to call the functions in .NET. Being a .NET developer affinity to both of these instructions is not must as .NET framework takes care of it. But we should be aware of what is happening inside the code and how these two IL instructions take care of calling the virtual and non virtual functions or rather I would say that this blog would help you to explain how virtual methods are being called in .NET framework.

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 the IL using ILDASM.exe for the instruction emitted, we can see 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()  
        {  
            return string.Empty;  
        }  
  
        public static string GetQualities()  
        {  
            return string.Empty;  
        }  
  
        public virtual string GetFeatures()  
        {  
            return string.Empty;  
        }  
  
        public override string ToString()  
        {  
            return "generic animal";  
        }  
  
    }  
  
    static void Main(string[] args)  
    {  
        Animal.GetQualities();  
  
        Animal person = new Animal();  
        person.GetFeatures();  
         person.GetAnimalType();              
              
    }

 

When compiler executed 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 type of call 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 thecall IL instruction assumes that the instance varible 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 null the 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 the to call the GetFeatures() virtual function of the class callvirt instruction is generated which is also in 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 compiler generates callvirt IL instruction to call the non-virtual function, that what 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)  
{  
    Console.WriteLine(var.ToString());  
}

Which I will call from my Main() function as shown below

GetString(person);

As we can see from the definition of the Animal class that ToString() function has been overriden. The IL code for the GetString(object var) is as following

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.

Interesting Anomaly

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()  
{  
    return base.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 must as if the ToString would have called virtually in that case the Animals ToString would have been called again and again resulting the thread’s stack overflow.

I hope I have been able to cover the topic of call and callvirt briefly in this blog. Please let me know your thought about this post

Top career enhancing courses you can't miss

My Learning Resource

Excel your system design interview