问题描述
我最近收到了以下代码作为一种谜题,以帮助理解 OOP - C# 中的多态性和继承.
I was recently given the following piece of code as a sort-of puzzle to help understand Polymorphism and Inheritance in 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!
现在,在与提出谜题的其他开发人员进行了长时间的交谈后,我知道输出是什么,但我不会为你剧透.我真正遇到的唯一问题是我们如何获得该输出,代码如何执行,继承什么等等.
Now, after a long, long chat with the other developer who presented the puzzle, I know what the output is, but I won't spoil it for you. The only issue I'm really having is how we get to that output, how the code steps through, what's inheriting what, etc.
我认为 C
会被返回,因为这似乎是定义的类.然后我想知道是否会返回 B
因为 C 继承了 B
- 但 B
也继承了 A
(这就是我感到困惑的地方!).
I thought C
would be returned as that seems to be the class that is defined. Then I went through my head as to whether B
would be returned because C inherits B
- but B
also inherits A
(which is where I got confused!).
谁能解释多态性和继承如何在检索输出中发挥作用,最终显示在屏幕上?
Could anyone explain how polymorphism and inheritance play their part in retrieving the output, eventually displayed on screen?
推荐答案
正确的思考方式是想象每个类都要求它的对象有一定数量的槽";这些插槽充满了方法.问题实际上调用了什么方法?"需要你弄清楚两件事:
The correct way to think about this is to imagine that every class requires its objects to have a certain number of "slots"; those slots are filled with methods. The question "what method actually gets called?" requires you to figure out two things:
- 每个插槽的内容是什么?
- 调用哪个槽?
让我们从考虑插槽开始.有两个插槽.A 的所有实例都需要有一个我们称之为 GetNameSlotA 的槽.C 的所有实例都必须有一个我们称之为 GetNameSlotC 的槽.这就是 C 中声明中新"的含义——它的意思是我想要一个新插槽".相比于 B 中声明的override",意思是我不想要新的 slot,我要重用 GetNameSlotA".
Let's start by considering the slots. There are two slots. All instances of A are required to have a slot we'll call GetNameSlotA. All instances of C are required to have a slot we'll call GetNameSlotC. That's what the "new" means on the declaration in C -- it means "I want a new slot". Compared to the "override" on the declaration in B, which means "I do not want a new slot, I want to re-use GetNameSlotA".
当然,C继承自A,所以C也必须有一个槽GetNameSlotA.因此,C 的实例有两个插槽——GetNameSlotA 和 GetNameSlotC.不是 C 的 A 或 B 的实例有一个插槽,GetNameSlotA.
Of course, C inherits from A, so C must also have a slot GetNameSlotA. Therefore, instances of C have two slots -- GetNameSlotA, and GetNameSlotC. Instances of A or B which are not C have one slot, GetNameSlotA.
现在,当您创建一个新的 C 时,这两个插槽中的内容是什么?共有三种方法,我们将它们称为 GetNameA、GetNameB 和 GetNameC.
Now, what goes into those two slots when you create a new C? There are three methods, which we'll call GetNameA, GetNameB, and GetNameC.
A 的声明说将 GetNameA 放入 GetNameSlotA".A 是 C 的超类,因此 A 的规则适用于 C.
The declaration of A says "put GetNameA in GetNameSlotA". A is a superclass of C, so A's rule applies to C.
B 的声明说将 GetNameB 放入 GetNameSlotA".B 是 C 的超类,因此 B 的规则适用于 C 的实例.现在我们在 A 和 B 之间发生冲突.B 是派生更多的类型,所以它获胜 -- B 的规则覆盖 A 的规则.因此声明中的覆盖"一词.
The declaration of B says "put GetNameB in GetNameSlotA". B is a superclass of C, so B's rule applies to instances of C. Now we have a conflict between A and B. B is the more derived type, so it wins -- B's rule overrides A's rule. Hence the word "override" in the declaration.
C 的声明说将 GetNameC 放入 GetNameSlotC".
The declaration of C says "put GetNameC in GetNameSlotC".
因此,您的新 C 将有两个插槽.GetNameSlotA 将包含 GetNameB,GetNameSlotC 将包含 GetNameC.
Therefore, your new C will have two slots. GetNameSlotA will contain GetNameB and GetNameSlotC will contain GetNameC.
我们现在已经确定了哪些方法在哪些槽中,所以我们已经回答了我们的第一个问题.
We've now determined what methods are in what slots, so we've answered our first question.
现在我们要回答第二个问题.叫什么槽?
Now we have to answer the second question. What slot is called?
把它想象成你是编译器.你有一个变量.您所知道的只是它属于 A 类型.您被要求解析对该变量的方法调用.您查看 A 上可用的插槽,您可以找到匹配的唯一插槽是 GetNameSlotA.你不知道GetNameSlotC,因为你只有一个A类型的变量;为什么要寻找只适用于 C 的插槽?
Think about it like you're the compiler. You have a variable. All you know about it is that it is of type A. You're asked to resolve a method call on that variable. You look at the slots available on an A, and the only slot you can find that matches is GetNameSlotA. You don't know about GetNameSlotC, because you only have a variable of type A; why would you look for slots that only apply to C?
因此,这是对 GetNameSlotA 中的任何内容的调用.我们已经确定在运行时,GetNameB 将位于该槽中.因此,这是对 GetNameB 的调用.
Therefore this is a call to whatever is in GetNameSlotA. We've already determined that at runtime, GetNameB will be in that slot. Therefore, this is a call to GetNameB.
这里的关键点是在 C# 重载解析中选择一个槽并生成对该槽中的任何内容的调用.
The key takeaway here is that in C# overload resolution chooses a slot and generates a call to whatever happens to be in that slot.
这篇关于为什么这个多态 C# 代码会打印它的功能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!