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",但我错了。对其他人也是错误的。
我没有详细说明我的问题,所以我再次尝试。
如果我考虑a.pqr():
执行将从yyy开始[我认为,需要纠正。] yyy.pqr()现在是虚拟的,它将查看覆盖的xxx.pqr()。它不应该停在这里吗? 或者它将遍历到www.pqr(),因为xxx.pqr()从yyy(基类)携带虚拟属性,它最终将在vvv.pqr()[实际类型] ??
对于c.pqr():
我可以帮助理解它为什么要打电话" vvv pqr"? 它的覆盖。它的基地还有联系吗?我看着它的覆盖,所以我认为答案是" www pqr"因为它不是虚拟的,它不会遍历,但确实如此。如果我使www.pqr()新,输出将是" www pqr"因为它新的,我不必低头。基本了解我处于两难境地。
答案 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.pqr
和xxx.pqr
是与www.pqr
和vvv.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的最派生实现相同。