无法在C#中使用相同方法的虚拟和覆盖

时间:2012-06-28 17:44:47

标签: c# polymorphism

显然,您无法将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的背景?

6 个答案:

答案 0 :(得分:25)

您可以将某个方法声明为virtual一次,但您可以override将它多次声明 - 覆盖不是最终的,并且它不会限制继承自第一次重写课程。最终将执行的方法是覆盖虚方法的最后一个方法。所以你的代码确实按预期运行。

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

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

这可能令人困惑,有时令人讨厌,但它可以确保你真正知道自己在做什么,这会让你的意图自我记录。

答案 1 :(得分:4)

  

显然你不能将虚拟修饰符与覆盖修饰符一起使用。

事实上。该方法保持虚拟,除非您使用声明为sealed的方法覆盖它。 (当你覆盖某些东西时,你可以对方法使用sealed修改。)来自C#4规范的第10.6.5节:

  

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

答案 2 :(得分:2)

我认为你只是误解了virtual是如何运作的。它不仅限于一个级别的继承,如here, for example所述:

  

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

答案 3 :(得分:2)

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

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

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

答案 4 :(得分:1)

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

答案 5 :(得分:1)

看起来它按预期工作。

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

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

  

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

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

  

所以Line中的draw方法看起来好像是Overrriden by   LittleLine

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