为什么我在覆盖和调用虚拟函数时没有出现堆栈溢出异常
本文关键字:堆栈 异常 栈溢出 函数 覆盖 虚拟 调用 为什么 | 更新日期: 2023-09-27 17:56:00
class Program
{
static void Main(string[] args)
{
B foo = new B();
foo.DoWork();
Console.ReadLine();
}
}
public class A
{
public virtual void DoWork() { Console.WriteLine("A"); }
}
public class B : A
{
public override void DoWork() { base.DoWork(); Console.WriteLine("B"); }
}
为什么我没有收到 StackOverflow 异常?据我了解,福。DoWork() 被调用,然后它调用 base。DoWork(),它是虚拟的,在类 B.DoWork() 方法中被覆盖,它会重复调用 base。再次执行 DoWork(),直到堆栈溢出。当使用它代替 base(调用 self 的循环)时,很容易实现这种溢出。在这种情况下,什么可以防止虚拟函数覆盖?
不,当您使用base
时,它不会进行虚拟调用。重点是能够调用base
实现,即使您已覆盖它。
如果您查看生成的 IL,您会发现它不使用 callvirt
:
IL_0002: call instance void A::DoWork()
来自 C# 5 规范的第 7.6.8 节(强调我的):
当基访问引用虚函数成员(方法、属性或索引器)时,在运行时调用哪个函数成员的决定 (§7.5.4) 将更改。调用的函数成员是通过查找函数成员相对于 B 的最派生实现 (§10.6.3) 来确定的(而不是相对于运行时类型,这在非基本访问中很常见)。因此,在虚拟函数成员的重写中,可以使用基访问来调用函数成员的继承实现。如果基访问引用的函数成员是抽象的,则会发生绑定时错误。
A.DoWork
是虚拟的。 但是base.
命名的方法从来都不是虚拟的。 该语法生成非虚拟调用,因此调用确切的方法,而不是派生最多的版本。
虚拟方法只是可以重写且包含代码的方法。当您调用base.DoWork()
时,您明确声明要调用A.DoWork()
。然后,调用A.DoWork()
。
尝试使A.DoWork()
抽象,然后它不能包含代码。然后,您将在base.DoWork()
上出现编译错误,因为base.DoWork()
中没有任何要执行的内容。