【问题标题】:Why don't I get stackoverflow exception in overriding and calling virtual function?为什么我在覆盖和调用虚函数时没有得到 stackoverflow 异常?
【发布时间】:2013-01-08 19:38:09
【问题描述】:
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 异常?据我了解,调用 foo.DoWork(),然后调用 base.DoWork(),它是虚拟的并在类 B.DoWork() 方法中被覆盖,它将再次重复调用 base.DoWork(),直到堆栈溢出.当使用 this 而不是 base(调用 self 的循环循环)时,很容易实现这种溢出。在这种情况下,是什么阻止了虚函数覆盖?

【问题讨论】:

  • 如果base 是这样工作的,那么任何时候使用它都会是一个stackoverflow,根本没有理由在语言中使用它。
  • @Servy 不,您可以使用 base 调用非虚拟方法而不会出现任何问题。这只是将 base 与虚拟方法一起使用的一个特例。
  • 如果你想调用基类的非虚拟方法,只需将this 转换为基类并调用该方法。 base 字面意思是专门为调用基类版本的虚方法而设计的,因为没有base 就无法完成,base 的任何其他用途都可以在没有该关键字的情况下完成。

标签: c# overriding stack-overflow virtual-functions


【解决方案1】:

A.DoWork 是虚拟的。但是base. 命名的方法是从不 虚拟的。该语法会生成一个非虚拟调用,因此调用的是确切的方法,而不是最派生的版本。

【讨论】:

    【解决方案2】:

    不,当您使用 base 时,它不会进行虚拟通话。重点是能够调用base 实现即使你已经覆盖它

    如果您查看生成的 IL,您会发现它没有使用 callvirt

    IL_0002:  call       instance void A::DoWork()
    

    来自 C# 5 规范的第 7.6.8 节(重点是我的):

    当基本访问引用虚拟函数成员(方法、属性或索引器)时,在运行时调用哪个函数成员的决定(第 7.5.4 节)会改变。 被调用的函数成员是通过查找函数成员相对于 B 的最派生实现 (§10.6.3) 来确定的(而不是相对于 this 的运行时类型,这在非基本访问)。因此,在一个虚函数成员的覆盖中,基本访问可用于调用函数成员的继承实现。如果基访问引用的函数成员是抽象的,则会发生绑定时错误。

    【讨论】:

    • Eric Lippert wrote 关于 C# 找到实际具有实现的最派生基类而不是直接基类这一事实的后果。
    • @BenVoigt:(我知道我们几乎在同一时间找到了它:)
    • 是的。我只是认为讨论似乎与这个答案有关。
    【解决方案3】:

    虚拟方法只是一种可以被覆盖并且包含代码的方法。当您调用base.DoWork() 时,您明确声明您要调用A.DoWork()。然后,A.DoWork() 被调用。

    尝试将A.DoWork() 抽象化,然后它就不能包含代码。然后,您将在base.DoWork() 上遇到编译错误,因为在base.DoWork() 中没有要执行的内容。

    【讨论】:

    • 重载!=被覆盖。你的意思是在这里被覆盖。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-08
    • 2020-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-13
    相关资源
    最近更新 更多