IL doesn’t always use callvirt instruction for virtual methods in a case like this:
class MakeMeASandwich{
public override string ToString(){
return base.ToString();
}
}
In this case, it is said that IL will produce call instead of callvirt where callvirt is produced to check whether variable is null or not and throws NullReferenceException otherwise.
- Why does a recursive invocation happen till stack overflow if
callvirtis used instead ofcall? - If
callis used, then when does it check whether the instance variable it uses to call the methods is null or not?
Because then your code is exactly the same as:
Which clearly is an infinite recursion, provided that the method given is the most-overriding version of ToString.
The question is not answerable because the question assumes a falsehood. The call instruction does not check to see if the reference to the receiver is null or not, so asking why the call instruction checks for null doesn’t make any sense.
Let me rephrase that for you into some better questions:
If the C# code is doing a non virtual call on a virtual method then the compiler must generate a call, not a callvirt. The only time this happens really is when using
baseto call a virtual method.If the C# code is doing a virtual call then the compiler must generate a callvirt.
If the C# code is doing a non virtual call on a non virtual method then the compiler can choose to generate either call or callvirt. Either will work. The C# compiler typically chooses to generate a callvirt.
No. The C# compiler can skip the null check if the receiver is already known to not be null. For example, if you said
(new C()).M()for a non-virtual method M then it would be legal for the compiler to generate acallinstruction without a null check. We know that (1) the method is not virtual, so it does not have to be acallvirt; we can choose whether to usecallvirtor not. And we know (2) thatnew C()is never null, so we do not have to generate a null check.If the C# compiler does not know that the receiver is not null, then it will either generate a callvirt, or it will generate a null check followed by a call.