Cla*_*Tan 21 .net c# inheritance
继承链如下:
class A
{
public virtual void Foo()
{
Console.WriteLine("A's method");
}
}
class B:A
{
public override void Foo()
{
Console.WriteLine("B's method");
}
}
class C:B
{
public new virtual void Foo()
{
Console.WriteLine("C's method");
}
}
class D:C
{
public override void Foo()
{
Console.WriteLine("D's method");
}
}
Run Code Online (Sandbox Code Playgroud)
然后:
class Program
{
static void Main(string[] args)
{
A tan = new D();
tan.Foo();
Console.Read();
}
}
Run Code Online (Sandbox Code Playgroud)
结果是,调用了B类中的方法foo().
但在参考中:
调用虚方法时,将检查对象的运行时类型是否有覆盖成员.如果没有派生类重写成员,则调用派生程度最大的类中的重写成员,该成员可能是原始成员.
在我的逻辑中,CLR首先发现Foo()是一个虚方法,它查看D运行时类型的方法表,然后它发现在这个最派生的类中有一个重写成员,它应该调用它并且永远不会意识到有一个new Foo()在继承链.
我的逻辑出了什么问题?
Eri*_*ert 34
艾米的回答是正确的.这是我喜欢看这个问题的方式.
虚方法是可以包含方法的插槽.
当要求进行重载解析时,编译器确定在编译时使用哪个插槽.但运行时确定该槽中实际的方法.
现在考虑到这一点,让我们看看你的例子.
A有一个插槽Foo.B有一个插槽Foo,继承自A.C有两个插槽Foo.一个继承自B,一个新的.你说你想要一个名为Foo的新插槽,所以你得到了它.D有两个插槽Foo,继承自C.那是插槽.那么,那些插槽里有什么?
A,A.Foo进入插槽.B,B.Foo进入插槽.C,B.Foo进入第一个插槽并C.Foo进入第二个插槽.请记住,这些插槽完全不同.你说你想要两个同名的插槽,这就是你得到的.如果那令人困惑,那就是你的问题.如果你这样做会伤害它,不要这样做.D,B.Foo进入第一个插槽并D.Foo进入第二个插槽.那么现在你的电话会怎么样?
编译器的原因是你正在调用Foo编译时类型A,所以它找到第一个(也是唯一的)Foo插槽A.
在运行时,该插槽的内容是B.Foo.
这就是所谓的.
现在有道理吗?
Amy*_*Amy 29
调用虚方法时,将检查对象的运行时类型是否有覆盖成员.如果没有派生类重写成员,则调用派生程度最大的类中的重写成员,该成员可能是原始成员.
你是从错误的地方开始的.您的变量是类型A并包含实例D,因此使用的虚拟表是A's 1.在上面的文字之后,我们检查一个压倒一切的成员.我们找到一个B. C不计算因为它没有覆盖,它是阴影基本方法.而且,由于D覆盖C,不是A或者B,它也不算重要.我们正在寻找最派生类中的重写成员.
所以发现的方法是B.Foo().
如果你改变C它覆盖而不是阴影,那么找到的方法将是D,因为它是派生最多的重写成员.
如果您改为将对象更改为B或的实例C,B.Foo()则仍将是所选的覆盖.澄清一下,这就是我的意思:
A tan = new B();
tan.Foo(); // which foo is called? Why, the best Foo of course! B!
Run Code Online (Sandbox Code Playgroud)
B调用的原因是因为我们正在搜索的继承链从A(变量类型)跨越到B(运行时类型). C并且D不再是该链的一部分,并且不属于虚拟表.
如果我们改为将代码更改为:
C tan = new D();
tan.Foo(); // which foo, which foo?
Run Code Online (Sandbox Code Playgroud)
我们搜索的继承链从跨越C到D. D是有一个压倒一切的成员,所以它Foo被称为.
假设您添加了另一个Q继承自A,R继承自Q,等等的类.你有两个继承分支,对吧?在搜索大多数派生类型时选择哪个?遵循从A(您的变量类型)到D(运行时类型)的路径.
我希望这是有道理的.
1不是字面意思.虚拟表属于,D因为它是运行时类型并包含其继承链中的所有内容,但它有用且更容易被A视为起点.毕竟,您正在搜索派生类型.