我最近收到了下面这段代码,作为一种谜题,帮助我理解OOP-C#中的PolymorphismInheritance.

// 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!

现在,在与提出这个谜题的其他开发人员进行了长时间的交谈之后,我知道输出是什么,但我不会为您 destruct 它.我真正遇到的唯一问题是我们如何获得输出,代码如何逐步完成,继承了什么,等等.

我认为会返回C,因为这似乎是定义的类.然后我想了想是否会返回B,因为C继承了B-但B也继承了A(这就是我感到困惑的地方!)


问题:

有人能解释一下多态性和继承如何在检索最终显示在屏幕上的输出中发挥作用吗?

推荐答案

思考这个问题的正确方法是假设每个类都要求其对象具有一定数量的"槽";这些插槽中充满了方法.问题是"实际上调用了什么方法?"需要你想出两件事:

  1. 每个位置的内容是什么?
  2. 叫哪个槽?

让我们先考虑一下插槽.有两个插槽.A的所有实例都需要有一个我们称之为GetNameSlotA的插槽.C的所有实例都需要有一个我们称之为GetNameSlotC的插槽.这就是C语言声明中"new"的意思——它的意思是"我想要一个新槽".与B中声明的"override"相比,B中的"override"意味着"我不想要一个新插槽,我想要重用GetNameSlotA".

当然,C继承自A,所以C还必须有一个插槽GetNameSlotA.因此,C的实例有两个插槽——GetNameSlotA和GetNameSlotC.非C的A或B实例有一个插槽GetNameSlotA.

现在,当你创建一个新的C时,这两个槽中会有什么?有三种方法,我们称之为GetNameA、GetNameB和GetNameC.

A的声明是"将GetNameA放入GetNameSlotA".A是C的超类,所以A的规则适用于C.

B的声明是"将GetNameB放入GetNameSlotA".B是C的超类,所以B的规则适用于C的实例.现在a和B之间存在冲突.B是更派生的类型,所以它获胜——B的规则overrides a的规则.因此,在声明中使用了"覆盖"一词.

C的声明称"将GetNameC放入GetNameSlotC".

因此,新的C将有两个插槽.GetNameSlotA将包含GetNameB,GetNameSlotC将包含GetNameC.

我们现在已经确定了哪些方法位于哪些插槽中,因此我们已经回答了第一个问题.

现在我们必须回答第二个问题.哪个位置叫什么?

想想看,就像你是编译器一样.你有一个变量.你只知道它是A型的.你被要求解析对该变量的方法调用.查看A上可用的插槽,可以找到唯一匹配的插槽是GetNameSlotA.你不知道GetNameSlotC,因为你只有一个a型变量;为什么要寻找只适用于C的插槽?

因此,这是对GetNameSlotA中任何内容的调用.我们已经确定,在运行时,GetNameB将位于该插槽中.因此,这是对GetNameB的调用.

这里的关键是in C# overload resolution chooses a slot,并生成对该插槽中发生的任何内容的调用.

.net相关问答推荐

MSBuild:CopyToOutputDirectory不会将本机DLL复制到输出

如何使用AWS Lambda函数制作网络挂钩?

带有ASP.NET核心的Angular 项目模板.API试验

如何通过在后台预加载流项来优化流迭代的性能?

FileHelpers 未使用正确的日期格式

在 .NET 中使用 AES 解密时缺少后半字节

您是否使用 TestInitialize 或测试类构造函数来准备每个测试?为什么?

具有透明背景且包含不透明控件的 WPF 窗口

.NET 中工作线程和 I/O 线程的简单描述

如何判断一个类型是否是简单类型?即持有一个单一的价值

控制台应用程序的退出时

无法将文件 *.mdf 作为数据库附加

操作对事务的状态无效错误和事务范围

何时在 C#/.NET 中使用指针?

C# 中的 F# List.map 类似功能?

并发字典正确用法

如何从 WCF REST 方法返回自定义 HTTP 状态代码?

安装带有恢复操作的 Windows 服务以重新启动

如何在不使用 3rd 方库的情况下登录 C#?

如何使用 XmlSerializer 将字符串序列化为 CDATA?