22

所以显然你不能将virtual修饰符与修饰符一起使用override

virtual- 一种可以被覆盖的方法

override- 覆盖其父类中同名方法的方法

这让我相信,如果我在子类中重写了一个方法,如果那个孩子有一个孩子,你就不能再重写那个方法了。

可以肯定地说,如果你在方法声明中放入overrideand ,你会在 C# 中得到一个编译错误。virtual

但是我不明白为什么我在下面制作的代码会以它的方式工作

using System;
public class DrawingObject
{
public virtual void Draw()
{
    Console.WriteLine("Drawing Object");
}
}
public class DrawDemo
{
     public static int Main()
     {
          DrawingObject[] dObj = new DrawingObject[3];


          dObj[0] = new DrawingObject();
           dObj[1] = new Line();
           dObj[2] = new LittleLine();

           foreach (DrawingObject drawObj in dObj)
           {
                drawObj.Draw();
           }
            Console.Read();
           return 0;
       }
  }
 public class Line : DrawingObject
 {
       public override void Draw()
       {// the method above me is in fact virtual because LittleLine overid it?
               Console.WriteLine("I'm a Line.");
       }
  }
  public class LittleLine : Line
  {
       public override void Draw()
       {
              Console.WriteLine("I'm a Little Line.");
         }
    }

这是输出:

绘图对象

我是一条线。

我是小线

所以中的 draw 方法Line看起来好像被LittleLine. 这段代码实际上并没有覆盖它,还是编译器做了其他一些技巧?还是我不理解virtualand的上下文override

4

6 回答 6

31

您可以将某个方法声明为virtual仅一次,但您可以override根据需要多次声明 - 覆盖不是最终的,并且它不限制从第一个覆盖类继承的类。最终将执行的方法是最后一个覆盖虚拟方法的方法。因此,您的代码确实按预期运行。

C# 在覆盖方面非常冗长——你有比 C++ 或 Java 更多的说明符。让程序员指定确切的意图是这样的:

  • virtual用来指定一个方法可以被子类覆盖。
  • override用来指定你正在重写一个你知道是虚拟的方法(如果不是,编译器会报告一个错误)。
  • 您用于sealed防止进一步覆盖。
  • 而且您使用new隐藏而不是覆盖。

这可能会令人困惑,有时也很烦人,但它可以确保您真正知道自己在做什么,并且可以让您的意图自我记录。

于 2012-06-28T17:50:04.620 回答
4

因此,显然您不能将 virtual 修饰符与 override 修饰符一起使用。

确实。该方法保持虚拟,除非您使用也声明为的方法覆盖它sealed。(当您覆盖某些内容时,您只能在方法上使用修改。)从 C# 4 规范的第 10.6.5 节:sealed

当实例方法声明包含密封修饰符时,该方法称为密封方法。如果实例方法声明包含sealed 修饰符,它还必须包含override 修饰符。使用sealed 修饰符可防止派生类进一步覆盖该方法。

于 2012-06-28T17:52:53.880 回答
2

我认为您只是误解了它virtual的工作原理。它不限于一级继承,如此处所述,例如

对于在类中声明或由类继承的每个虚拟方法,都存在该方法相对于该类的最派生实现。虚拟方法 M 相对于类 R 的最派生实现如下确定:
• 如果 R 包含 M 的引入虚声明,则这是 M 的最派生实现。
• 否则,如果 R 包含M,那么这是 M 的最派生实现。
• 否则,M 相对于 R 的最派生实现与 M 相对于 R 的直接基类的最派生实现相同。

于 2012-06-28T17:53:19.687 回答
2

这种行为是对的。如果您不希望继承的类覆盖您的虚拟方法,则可以将其标记为“密封”,如下所示:

public class Line : DrawingObject
 {
       public sealed override void Draw()
       {
               Console.WriteLine("I'm a Line.");
       }
  }

之后 LittleLine 类将无法覆盖 Draw 方法。

于 2012-06-28T17:54:13.000 回答
1

virtual表示您的方法是通过虚拟方法表调度的。如果方法是虚拟的,则派生类上对该方法的任何引用都必须通过 vtable。您不能仅仅因为派生类覆盖它而对其进行非虚拟化 - 如果LittleLine强制转换为会发生什么DrawingObject?您仍然需要找到正确的实现,而不是DrawingObject's

于 2012-06-28T17:52:32.430 回答
1

看起来它按预期工作。

dObj[0] = new DrawingObject();
dObj[1] = new Line();
dObj[2] = new LittleLine();

您正在调用 Draw() 并在循环中

继承人输出绘图对象我是一条线。我是小线

dObj[0].Draw() -> "绘图对象" dObj[1].Draw() -> "我是一条线。" dObj[2].Draw() -> "我是一条小线"

所以 Line 中的 draw 方法看起来好像被 LittleLine 覆盖了

是的,这不正是你所期望的吗?该方法在基类中被标记为 Virtual 并且您已经覆盖了它。如果您想“添加”,则需要使用其他模式。也许是一个装饰师。

于 2012-06-28T17:53:54.570 回答