【问题标题】:Virtual and override虚拟和覆盖
【发布时间】:2016-02-10 20:11:04
【问题描述】:

我来自 C++ 世界,您可能知道那里只有一个虚拟方法关键字,即 virtual。因此,在 C++ 中,您使用相同的 virtual 关键字重新定义派生类中的虚拟方法(在 C# 中,您使用 override)。我完全了解多态性以及“覆盖”和new 如何在 C# 中工作。但是,我无法在书中找到为基类和派生类中的虚拟方法创建两个不同关键字的想法,即virtualoverride 对应?还是只是为了清楚起见?

【问题讨论】:

  • 你的 C++ 知识有点过时了。从 C++11 开始,如果您使用 override 关键字,编译器知道如何交叉检查被覆盖的虚函数的签名(请注意,它与 virtual 放在函数签名中的位置不同)
  • 如果您使用new,每个类的基类或派生类都会调用它们自己的方法。如果您使用override 基类和继承类,请使用覆盖方法(在派生类中)。

标签: c# virtual-functions


【解决方案1】:

a 2003 interview 中,Anders Hejlsberg(C# 的首席架构师)解释说,决定使用两个不同的关键字主要是为了解决版本控制问题,即基础组件在不同版本之间发生变化。用他自己的话说(强调我的):

我可以向您展示一个非常真实的版本控制问题,我们现在确实可以从 Java 的经验中看到这个问题。每当他们发布新版本的 Java 类库时,就会发生损坏。每当他们在基类中引入新方法时,如果派生类中的某个人有同名的方法,则该方法现在是一个覆盖——除非它具有不同的返回类型,否则它不再编译。 问题在于 Java 和 C++ 都没有捕捉到程序员关于虚拟的意图。

当您说“虚拟”时,您可以表示两种情况之一。如果您没有继承具有相同签名的方法,那么这是一个新的虚拟方法。这是一个意思。否则,它是继承方法的覆盖。那是另一个意思。

从版本控制的角度来看,程序员在声明虚拟方法时表明他们的意图很重要。 例如,在 C# 中,您必须明确指出您想要虚拟的含义。要声明一个新的虚拟方法,只需将其标记为virtual。但是要覆盖现有的虚拟方法,你必须说override

因此,C# 没有我之前描述的特定版本控制问题,我们在基类中引入了派生类中已有的方法。在你的课堂上,你会声明foo virtual。现在我们介绍一个新的虚拟foo。嗯,没关系。现在有两个虚拟foos。有两个 VTBL 插槽。派生的foo 隐藏了基础foo,但这很好。在编写派生的foo 时,基础foo 甚至都不存在,所以隐藏这个新功能并没有什么问题。事情继续按照他们应该的方式运作。

所以,你正在扩展一个类。您将虚拟方法添加到您的类。在以后的版本中,基类添加了自己的虚拟方法,并且它的签名与您的虚拟方法的签名相匹配。这可能是完全偶然发生的,也可能是因为您的方法碰巧实际上服务于相关目的(并且您只是在基类设计者之前解决了特定需求)。无论如何,情况是模棱两可的。

由于存在两个不同的关键字(virtualoverride),C# 编译器可以为您提供不间断的行为(通过隐藏继承的虚拟方法并将其与您的虚拟方法分开)并带来通过此警告引起您注意的问题:

'Derived.Foo()' 隐藏继承的成员'Base.Foo()'。要使当前成员覆盖该实现,请添加 override 关键字。否则添加 new 关键字。

它现在要求您通过明确表达意图来解决问题:从现在开始,您是否覆盖已添加的继承方法 (override)?或者你还在开始自己的虚拟方法,现在隐藏了继承的方法(virtualnew)?

【讨论】:

  • 非常感谢。这回答了我的问题。
【解决方案2】:

这是个好问题。

您使用override 关键字来覆盖虚拟方法,因为您实际上可以在派生类中定义第二个virtual 方法,其签名与基类虚拟方法相同,并且也可以被覆盖。

这是来自MSDN的实际示例:

using System;
class A
{
   public virtual void F() { Console.WriteLine("A.F"); }
}
class B: A
{
   public override void F() { Console.WriteLine("B.F"); }
}
class C: B
{
   new public virtual void F() { Console.WriteLine("C.F"); }
}
class D: C
{
   public override void F() { Console.WriteLine("D.F"); }
}
class Test
{
   static void Main() {
      D d = new D();
      A a = d;
      B b = d;
      C c = d;
      a.F();
      b.F();
      c.F();
      d.F();
   }
} 

产量:

B.F
B.F
D.F
D.F

因为:

C 和 D 类包含两个具有相同的虚方法 签名:A 介绍的签名和 C 介绍的签名。 C 引入的方法隐藏了从 A 继承的方法。因此, D 中的 override 声明覆盖了 C 引入的方法,它 D 无法覆盖 A 引入的方法。

【讨论】:

  • 酷,这是一个完美的解释。谢谢。
【解决方案3】:

为了清楚起见,我假设。我个人更喜欢在更改父类的行为时覆盖。此外,我相信它更清楚地说明了 C# 不允许多重继承,而 C++ 中的类可以同时继承自祖父类和父类。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-25
    • 1970-01-01
    • 1970-01-01
    • 2018-07-26
    • 2012-06-19
    • 2011-11-13
    • 2014-11-14
    • 1970-01-01
    相关资源
    最近更新 更多