IL为调用函数提供了两个语句,即call和callvirt。调用用于调用非虚函数或静态函数或编译器不希望对引用进行空检查的任何函数。
callvirt用于调用虚函数,也调用非虚函数,因为编译器在运行时对引用进行空检查。
现在,在通过C#进行CLR时,我发现了以下示例。
internal class SomeClass
{
public override String ToString()
{
return base.ToString();
}
}
现在ToString()是虚函数,但是编译器为它生成调用指令就ok了。但是Jeffrey之所以提到为什么没有生成callvirt的原因是因为在这种情况下ToString()将被递归调用并将导致StackOverFlow例外,我试图理解,但无法围绕这个想法?任何人都可以解释为什么它会导致递归调用吗?
谢谢..
答案 0 :(得分:2)
对特定超类的显式调用(在本例中为System.Object
,因为您编写了base
)必须不为callvirt
,因为这可能导致堆栈溢出。
一些C#伪代码:
internal class SomeClass
{
public override String ToString()
{
// The "return base.ToString()" call could produce one of these two possibilities:
// This will NOT go through the class hierarchy, searching for a overwritten function
// called ToString
call and return System.Object::ToString()
// But this WILL, thus calling SomeClass::ToString() recursively, so this is wrong
// and would lead to a stack overflow
callvirt and return System.Object::ToString()
}
}
希望这就是你的意思。
答案 1 :(得分:1)
据我所知,如果编译器生成callvirt,则会发生stackoverflow异常,因为:
某些代码调用类型为 someclass 的对象的ToString,它继承自类 object 。 * somclass“方法的ToString调用它的基类的ToString方法,它是 object 。
如果此调用 是虚拟的,则不会导致从类对象调用ToString,而是调用实际类的ToString(这是 SomeClass的)。
然后你会处于一个不定式的循环中,因为现在整个事情将从新的开始。