c# – 为什么callvirt IL指令会导致虚拟方法中的递归调用?

在这种情况下,IL并不总是将callvirt指令用于虚拟方法
class MakeMeASandwich{
  public override string ToString(){
    return base.ToString();
  }
}

在这种情况下,据说IL将生成调用而不是callvirt,其中生成callvirt以检查变量是否为null,否则抛出NullReferenceException.

>如果使用callvirt而不是call,如果堆栈溢出,为什么会发生递归调用
>如果使用了call,那么它何时检查它用于调用方法的实例变量是否为null?

解决方法

Why does a recursive invocation happen till stack overflow if callvirt is used instead of call?

因为那时你的代码完全相同:

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

这显然是无限递归,只要给出的方法是ToString最重要的版本.

If call is used,then how come it checks whether the instance variable it uses to call the methods is null or not?

这个问题无法回答,因为这个问题假设是虚假的.调用指令不检查对接收器的引用是否为空,因此询问调用指令检查为null的原因没有任何意义.

让我重新解释一下,为你提出一些更好的问题:

Under what circumstances does the C# compiler generate a call vs a callvirt?

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

如果C#代码正在进行虚拟调用,那么编译器必须生成一个callvirt.

如果C#代码在非虚拟方法上执行非虚拟调用,则编译器可以选择生成call或callvirt.要么工作. C#编译器通常选择生成callvirt.

The call instruction does not automatically do a null check,but callvirt does. If the C# compiler chooses to generate a call instead of a callvirt,is it also obligated to generate a null check?

不可以.如果已知接收器不为空,则C#编译器可以跳过空检查.例如,如果你说(新的C()).M()用于非虚方法M那么编译器生成一个没有空检查的调用指令是合法的.我们知道(1)该方法不是虚拟的,因此它不必是一个callvirt;我们可以选择是否使用callvirt.我们知道(2)新的C()永远不会为空,所以我们不必生成空检查.

如果C#编译器不知道接收器不为null,那么它将生成一个callvirt,或者它将生成一个空检查,然后调用.

相关文章

在要实现单例模式的类当中添加如下代码:实例化的时候:frmC...
1、如果制作圆角窗体,窗体先继承DOTNETBAR的:public parti...
根据网上资料,自己很粗略的实现了一个winform搜索提示,但是...
近期在做DSOFramer这个控件,打算自己弄一个自定义控件来封装...
今天玩了一把WMI,查询了一下电脑的硬件信息,感觉很多代码都...
最近在研究WinWordControl这个控件,因为上级要求在系统里,...