在这种情况下,IL并不总是将callvirt指令用于virtual方法:

class MakeMeASandwich{
  public override string ToString(){
    return base.ToString();
  }
}

在这种情况下,据说IL将生成call而不是callvirt,其中会生成callvirt来检查variable是否为null,否则抛出NullReferenceException
  • 如果使用callvirt而不是call,为什么在堆栈溢出之前会进行递归调用?
  • 如果使用call,则何时检查用于调用方法的实例变量是否为null?
  • 最佳答案



    因为这样,您的代码与以下代码完全相同:

    override string ToString()
    {
        return this.ToString();
    }
    

    如果给定的方法是ToString的最主要版本,则这显然是无限递归。



    该问题无法回答,因为该问题具有虚假性。调用指令不会检查接收方的引用是否为null,因此询问为什么调用指令检查null毫无意义。

    让我为您改写一些更好的问题:



    如果C#代码正在虚拟方法上进行非虚拟调用,则编译器必须生成一个调用,而不是callvirt。真正发生这种情况的唯一时间是使用base调用虚拟方法时。

    如果C#代码正在执行虚拟调用,则编译器必须生成一个callvirt。

    如果C#代码正在非虚拟方法上进行非虚拟调用,则编译器可以选择生成call或callvirt。任何一个都可以。 C#编译器通常选择生成callvirt。



    否。如果已知接收者不为空,则C#编译器可以跳过空检查。例如,如果您说的是非虚拟方法M的(new C()).M(),则编译器在不进行空检查的情况下生成call指令是合法的。我们知道(1)该方法不是虚拟的,因此它不必是callvirt;我们可以选择是否使用callvirt。而且我们知道(2)new C()永远不会为空,因此我们不必生成空检查。

    如果C#编译器不知道接收方不为null,则它将生成一个callvirt,或者将生成一个null检查,然后进行调用。

    关于c# - 为什么callvirt IL指令会导致虚拟方法中的递归调用?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10213413/

    10-10 17:58