【问题标题】:Can't use virtual and override on the same method in C#不能在 C# 中的同一方法上使用虚拟和覆盖
【发布时间】:2012-06-28 17:44:46
【问题描述】:

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

virtual - 可以被覆盖的方法

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

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

可以肯定地说,如果您将overridevirtual 放在方法声明中,您将在C# 中得到编译错误。

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

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.");
         }
    }

这是输出:

绘图对象

我是一条线。

我是一条小线。

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

【问题讨论】:

  • override 方法再次被自动覆盖 (virtual)。
  • 正如其他人所说,sealed override 不允许在派生类中进一步覆盖该方法。另一个有趣的花絮:abstract override 强制派生类覆盖该方法。您的类可能不提供基类的虚方法的覆盖实现。但是,您可以强制派生自您的类覆盖它。
  • 感谢您的澄清!

标签: c# polymorphism


【解决方案1】:

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

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

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

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

【讨论】:

  • 所以当在 LittleLINE 对象上绘图时被调用。编译器是否在查看对象时发现它有一个 draw 方法并每天调用它?还是看littleLine对象,看它是Line的基类,再看它是DrawingObject的基类,从根parents方法开始再往下走。
  • 即有没有办法像 super.draw() 那样做,或者在类本身之外的任何地方调用该对象的父方法?即做 ((Line)dObj[2]).draw() 仍然调用draw的小线实现。有没有办法让父母实施你覆盖或没有的方法
  • @BobSinclar,据我所知,没有办法从外部调用重写的方法(请参阅this question。这可能使用反射,但即使是这样,在最好的。
  • 简短总结更多知识。 :-) 谢谢。
【解决方案2】:

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

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

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

【讨论】:

【解决方案3】:

我认为您只是误解了virtual 的工作原理。正如here, for example 解释的那样,它不仅限于一级继承:

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

【讨论】:

    【解决方案4】:

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

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

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

        它看起来像预期的那样工作。

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

        你在循环中调用 Draw()

        这是输出绘图对象我是一条线。我是一条小线

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-01-28
          • 2011-05-21
          • 2011-08-16
          • 2013-01-15
          • 1970-01-01
          • 2017-09-08
          相关资源
          最近更新 更多