C# - 虚拟,覆盖 - 执行路径

时间:2014-06-02 12:58:14

标签: c# oop

class zzz
{
    public static void Main()
    {
        yyy a = new vvv();
        xxx b = new vvv();
        www c = new vvv();
        vvv d = new vvv();

        a.pqr();
        b.pqr();
        c.pqr();
        d.pqr();
    }
}
class yyy
{
    public virtual void pqr()
    {
        System.Console.WriteLine("yyy pqr");
    }
}
class xxx : yyy
{
    public override void pqr()
    {
        System.Console.WriteLine("xxx pqr");
    }
}
class www : xxx
{
    public override void pqr()
    {
        System.Console.WriteLine("www pqr");
    }
}
class vvv : www
{
    public override void pqr()
    {
        System.Console.WriteLine("vvv pqr");
    }
}

输出:

 vvv pqr
 vvv pqr
 vvv pqr
 vvv pqr

我知道我要问的问题会让我获得一些投票。但我没有别的选择。有人可以帮我理解这段代码的执行路径吗? 我认为a.pqr()会停在" xxx pqr",但我错了。对其他人也是错误的。

我没有详细说明我的问题,所以我再次尝试。

  1. 如果我考虑a.pqr():

    执行将从yyy开始[我认为,需要纠正。] yyy.pqr()现在是虚拟的,它将查看覆盖的xxx.pqr()。它不应该停在这里吗? 或者它将遍历到www.pqr(),因为xxx.pqr()从yyy(基类)携带虚拟属性,它最终将在vvv.pqr()[实际类型] ??

  2. 对于c.pqr():

    我可以帮助理解它为什么要打电话" vvv pqr"? 它的覆盖。它的基地还有联系吗?我看着它的覆盖,所以我认为答案是" www pqr"因为它不是虚拟的,它不会遍历,但确实如此。如果我使www.pqr()新,输出将是" www pqr"因为它新的,我不必低头。基本了解我处于两难境地。

2 个答案:

答案 0 :(得分:1)

您正在使用虚拟方法调度。由于您的方法是虚拟的并且您永远不会隐藏它,因此它将始终调用实际类型的方法体,而不是变量的类型。

如果您使用new代替override,则会打印出来:

yyy pqr
xxx pqr
www pqr
vvv pqr

调用的方法将在编译时确定 - 在编译时,您将使用变量类型而不是实际的运行时类型。

这意味着到达你的答案"需要你这样做:

class www : xxx
{
    public new void pqr()
    {
        System.Console.WriteLine("www pqr");
    }
}

现在虚拟方法"链"在www处被破解 - 方法yyy.pqrxxx.pqr是与www.pqrvvv.pqr不同的虚拟方法。

然而,最有可能的是,这是一种非常错误的行为,因为它违反了面向对象的编程原则 - 调用的方法取决于变量的类型,它应该依赖于对象的类型。这就是为什么编译器在没有使用new的情况下隐藏方法时会警告你的原因,这就是为什么你应该非常谨慎地使用new - 大多数情况下它只是意味着你的对象设计是错误的。

即便如此,您的覆盖实际上会丢弃父方法的整个代码。这通常不是一个很好的做法 - 更常见的模式是将父方法作为子方法执行的一部分来调用。例如:

class yyy
{
    public virtual void pqr()
    {
        System.Console.WriteLine("yyy pqr");
    }
}
class xxx : yyy
{
    public override void pqr()
    {
        base.pqr();

        System.Console.WriteLine("xxx pqr");
    }
}
class www : xxx
{
    public override void pqr()
    {
        base.pqr();

        System.Console.WriteLine("www pqr");
    }
}
class vvv : www
{
    public override void pqr()
    {
        base.pqr();

        System.Console.WriteLine("vvv pqr");
    }
}

现在,如果您致电new vvv().pqr(),您将获得以下输出:

yyy pqr
xxx pqr
www pqr
vvv pqr

答案 1 :(得分:1)

该方法在yyy中声明为虚拟,并在每个后代类中重写。就CLR而言,这是一种具有大量实现的方法。编译器发出对该方法的调用,并且在执行时CLR将考虑调用该方法的对象的实际类型,并执行覆盖最接近该类型的对象的实现。 / p>

从C#5规范,第10.6.3节:

  

在虚方法调用中,进行该调用的实例的运行时类型决定了要调用的实际方法实现。

     

...

     

对于在类中声明或继承的每个虚方法,该方法存在与该类相关的最多派生实现。关于类R的虚拟方法M的最派生的实现确定如下:

     
      
  • 如果R包含M的引入虚拟声明,那么这是M的最多派生实现。
  •   
  • 否则,如果R包含M的覆盖,则这是M的最多派生实现。
  •   
  • 否则,关于R的M的最派生实现与关于R的直接基类的M的最派生实现相同。
  •