我最近给下面的一段代码作为排序的拼图,以帮助了解Polymorphism
和Inheritance
的OOP - C#。
// No compiling!
public class A
{
public virtual string GetName()
{
return "A";
}
}
public class B:A
{
public override string GetName()
{
return "B";
}
}
public class C:B
{
public new string GetName()
{
return "C";
}
}
void Main()
{
A instance = new C();
Console.WriteLine(instance.GetName());
}
// No compiling!
现在,谁提出难题的其他开发人员很长很长的聊天后,我知道输出是什么,但我不会破坏它。 我真的有唯一的问题是我们如何达到那个输出的代码是如何一步一步地,什么继承什么,等等。
我认为C
为似乎是被定义的类将被退回。 然后,我通过我的头去,是否B
因为C继承了将返回B
-但B
也继承了A
(!这是我糊涂了)。
题:
谁能解释多态和继承的检索输出,最终显示在屏幕上如何发挥作用?
想想这个正确的方法是想象,每类需要它的对象有一定的数量的“槽”的; 这些时隙被填充方法。 这个问题:“实际上被调用什么方法?” 你需要弄清楚两件事情:
- 什么是每个槽的内容是什么?
- 哪个插槽叫什么名字?
让我们先考虑的插槽开始。 有两个槽。 A的所有实例都需要有一个插槽,我们会打电话给GetNameSlotA。 C:的所有实例都需要有一个插槽,我们会打电话给GetNameSlotC。 这就是“新”在C中的声明意味着 - 它的意思是“我想要一个新的槽”。 相比于B,这意味着声明的“覆盖”“我不想要一个新的插槽,我想重新使用GetNameSlotA”。
当然,C从A继承,所以C也必须有一个槽GetNameSlotA。 因此,将C的实例具有两个狭槽 - GetNameSlotA,和GetNameSlotC。 A的实例或B,其不是C有一个狭槽,GetNameSlotA。
现在,当你创建一个新的C什么进入这两个插槽? 有三种方法,我们称之为GetNameA,GetNameB和GetNameC。
A的声明说:“把GetNameA在GetNameSlotA”。 A是C的父类,所以A的规则适用于C.
B的声明中说:“把GetNameB在GetNameSlotA”。 B是C的父类,那么B的规则适用于C的情况下,现在我们有A和B B之间的冲突是比较派生类型,因此它赢得- B的规则将覆盖 A的规则。 因此,在该声明中的单词“覆盖”。
C的声明说:“把GetNameC在GetNameSlotC”。
因此,您的新的C将有两个槽。 GetNameSlotA将包含GetNameB和GetNameSlotC将包含GetNameC。
现在,我们已经确定在什么插槽什么方法,所以我们已经回答了第一个问题。
现在我们就来回答第二个问题。 什么槽被调用?
想想看,就像你的编译器。 你有一个变量。 所有你知道它是,它是A型。你被要求解决该变量的方法的调用。 你看看插槽可在A,你可以找到匹配的唯一插槽GetNameSlotA。 你不知道GetNameSlotC,因为你只有一个类型的变量; 为什么你会寻找那些只适用于C插槽?
因此,这是无论是在GetNameSlotA通话。 我们已经确定,在运行时,GetNameB将在该插槽。 因此,这是GetNameB通话。
这里的关键是外卖, 在C#重载解析选择了一个插槽 ,并生成调用无论发生什么事要在该插槽。
因为它应该返回“B” B.GetName()
中的小虚表盒保持A.GetName()
函数。 C.GetName()
是一个编译时“覆盖”,它不重写虚表,所以你无法通过指针检索A
。
很简单,你只需要保持继承树的初衷。
在你的代码,你持有一类类型的“A”,这是类型的“C”的实例实例的引用。 现在,为了解决虚拟'的GetName()方法的精确方法的地址,编译器会沿继承层次结构,并寻找最近的倍率 (请注意,只有“虚拟”是一个覆盖,“新”是完全不同的东西...)。
这在短期会发生什么。 从类型“C”新的关键字只能起到一定的作用,如果你把它在类型“C”和编译器的实例,然后将完全否定所有可能的继承关系。 严格来说,这有什么好跟多态性都这样做 - 你可以看到一个事实,即不管你掩盖虚拟或非虚方法与“新”的关键字没有任何区别...
“新”类“C”是指正是:如果你所说的“的GetName()”在这方面的一个实例(精确)类型,然后忘了一切,并使用此方法。 在相反的“虚拟”的意思是:你上去继承树,直到找到一个方法使用这个名称,无论调用实例的确切类型是什么。
OK,后是有点老了,但它是一个很好的问题和出色答卷,所以我只是想补充我的想法。
考虑下面的例子,这是与以前一样,除了主要功能:
// No compiling!
public class A
{
public virtual string GetName()
{
return "A";
}
}
public class B:A
{
public override string GetName()
{
return "B";
}
}
public class C:B
{
public new string GetName()
{
return "C";
}
}
void Main()
{
Console.Write ( "Type a or c: " );
string input = Console.ReadLine();
A instance = null;
if ( input == "a" ) instance = new A();
else if ( input == "c" ) instance = new C();
Console.WriteLine( instance.GetName() );
}
// No compiling!
现在,它真的很明显,函数调用不能绑定在编译时特定的功能。 一定有什么然而编译,并且这些信息只能依赖于引用的类型。 所以,这将是不可能的比C型中的一个之外的任何参考来执行C级的函数的GetName
PS也许我应该用在功能代替术语的方法,但正如莎士比亚所说:任何其他名称的功能仍然是一个功能:)
实际上,我认为它应该显示C,因为新的运营商只是隐藏了所有同名的祖先方法。 所以,用A的方法和B隐藏的,只有C保持可见。
http://msdn.microsoft.com/en-us/library/51y09td4%28VS.71%29.aspx#vclrfnew_newmodifier