虚拟和覆盖

时间:2016-02-10 20:11:04

标签: c# virtual-method

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

3 个答案:

答案 0 :(得分:4)

这是一个很好的问题。

您使用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中的覆盖声明覆盖了C和它引入的方法   D不能覆盖A引入的方法。

答案 1 :(得分:4)

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

  

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

     

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

     

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

     

因此,C#没有我前面描述的特定版本控制问题,我们在基类中引入了一个方法,你已经在派生类中使用了它。在你的课堂上,你会声明foo虚拟。现在我们介绍一个新的虚拟foo。嗯,那没关系。现在有两个虚拟foo。有两个VTBL插槽。派生的foo隐藏了基础foo,但这没关系。当编译派生的foo时,基础foo甚至不存在,所以隐藏这个新功能并不是什么问题。事情继续以他们应有的方式发挥作用。

所以,你正在扩展一个课程。您可以在类中添加虚拟方法。在更高版本中,基类添加自己的虚方法,其签名与虚方法的签名匹配。这可能是由于完全意外发生,或者因为您的方法碰巧实际上用于相关目的(并且您只是在基类设计者之前解决了特定需求)。在任何情况下,情况都不明确。

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

  

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

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

答案 2 :(得分:0)

我为了清楚而假设。我个人更喜欢在更改父类的行为时使用覆盖。此外,我相信它提供了更清晰的事实,即C#不允许多重继承,可以理解,C ++中的类可以从Grand Parent和Parent类继承。