Невозможно использовать виртуальные и переопределить один и тот же метод в С#

По-видимому, вы не можете использовать модификатор virtual с модификатором override.

virtual - метод, который можно переопределить

override - метод, который переопределяет метод с тем же именем в его родительском классе

Это заставляет меня думать, что если я переопределю метод в дочернем классе, если у этого ребенка есть ребенок, вы не сможете переопределить этот метод еще раз.

И можно с уверенностью сказать, что если вы введете override и 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. Этот код на самом деле не переопределяет его, или компилятор делает какой-то другой трюк? Или я не понимаю контекст virtual и override?

Ответ 1

Вы можете объявить определенный метод как virtual только один раз, но вы можете override его столько раз, сколько хотите - переопределение не является окончательным, и оно не ограничивает классы, наследующие от первого класса переопределения. Метод, который в конечном итоге будет выполнен, является последним, который переопределяет виртуальный метод. Таким образом, ваш код работает как ожидалось.

С# очень многословно относится к переопределению - у вас больше спецификаций, чем на С++ или Java. Это так, чтобы программист указал точное намерение:

  • Вы используете virtual, чтобы указать, что метод может быть переопределен подклассами.
  • Вы используете override, чтобы указать, что вы переопределяете метод, который, как вам известно, является виртуальным (если это не так, компилятор сообщит об ошибке).
  • Вы используете sealed, чтобы предотвратить дальнейшее переопределение.
  • И вы используете new, чтобы скрыть вместо переопределения.

Это может быть запутанным, а иногда и раздражающим, но оно гарантирует, что вы действительно знаете, что делаете, и оно делает ваше намерение самодокументированным.

Ответ 2

По-видимому, вы не можете использовать виртуальный модификатор с модификаторами переопределения.

Действительно. Метод остается виртуальным, если вы не переопределите его с помощью метода, который также объявлен как sealed. (Вы можете использовать модификацию sealed для метода, когда вы переопределяете что-то.) Из раздела 10.6.5 спецификации С# 4:

Когда объявление метода экземпляра включает в себя запечатанный модификатор, этот метод считается закрытым методом. Если объявление метода экземпляра включает в себя модифицированный модификатор, он также должен включать модификатор переопределения. Использование запечатанного модификатора предотвращает последующее переопределение производного класса.

Ответ 3

Я думаю, вы просто неправильно поняли, как работает virtual. Это не ограничивается одним уровнем наследования, как описано здесь, например:

Для каждого виртуального метода, объявленного или унаследованного классом, существует наиболее производная реализация метода по отношению к этому классу. Наиболее производная реализация виртуального метода M относительно класса R определяется следующим образом:
 • Если R содержит введенное виртуальное объявление M, то это самая производная реализация M.
• В противном случае, если R содержит переопределение M, то это самая производная реализация M.
• В противном случае наиболее производная реализация M относительно R такая же, как и самая производная реализация M относительно прямого базового класса R.

Ответ 4

virtual означает, что ваш метод отправляется через таблицу виртуальных методов. Если метод виртуальный, любая ссылка на этот метод на производном классе должна проходить через таблицу vtable. Вы не можете не виртуализировать его только потому, что производный класс переопределяет его - что произойдет, если LittleLine был добавлен в DrawingObject? Вам все равно нужно найти правильную реализацию, которая не была бы DrawingObject

Ответ 5

Похоже, он работал по назначению.

dObj [0] = новый DrawingObject();
dObj [1] = новая строка(); dObj [2] = new LittleLine();

Вы вызываете Draw() и в цикле

heres output Drawing Object я is a Line. Я Маленькая Линия

dObj [0].Draw() → "Объект рисования" dObj [1].Draw() → "Я - линия". dObj [2].Draw() → "Я маленькая линия"

Таким образом, метод draw в Line выглядит так, как будто он был Overrriden by LittleLine

Да, разве это не то, чего вы ожидали? Этот метод был отмечен как Virtual в базовом классе, и вы его переопределили. Если вы хотите "добавить к нему", вам нужно будет использовать другой шаблон. Возможно, декоратор.

Ответ 6

Это правильно. Если вы не хотите, чтобы унаследованный класс переопределял ваш виртуальный метод, вы можете отметить его "запечатанным" следующим образом:

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

После этого класс LittleLine не сможет переопределить метод Draw.