Дизайн С#: Почему новый или переопределяемый требуется для абстрактных методов, но не для виртуальных методов?
Почему для абстрактных методов требуется новое/переопределенное, но не виртуальные методы?
Пример 1:
abstract class ShapesClass
{
abstract public int Area(); // abstract!
}
class Square : ShapesClass
{
int x, y;
public int Area() // Error: missing 'override' or 'new'
{
return x * y;
}
}
Компилятор покажет эту ошибку:
Чтобы заставить текущий элемент переопределить эту реализацию, добавьте ключевое слово переопределения. В противном случае добавьте новое ключевое слово
Пример 2:
class ShapesClass
{
virtual public int Area() { return 0; } // it is virtual now!
}
class Square : ShapesClass
{
int x, y;
public int Area() // no explicit 'override' or 'new' required
{
return x * y;
}
}
Это будет компилироваться отлично, скрыв метод по умолчанию.
Я полностью понимаю технические различия. Однако мне интересно, почему язык был разработан именно так. Не лучше ли было бы иметь такое же ограничение в "Образце 2"? В большинстве случаев, если вы создаете метод с тем же именем, что и в родительском классе, вы обычно намерены его переопределить. Поэтому я думаю, что явное указание Override/New имеет смысл и для виртуальных методов.
Существует ли конструктивная причина такого поведения?
Update:
Второй пример фактически вызывает предупреждение. Первый пример показывает ошибку, потому что подкласс необходим для реализации абстрактного метода. Я не видел предупреждения в VS.. теперь имеет для меня смысл. Спасибо.
Ответы
Ответ 1
Используя либо компилятор С# 3.0, поставляемый в .NET 3.5 SP1, либо компилятор С# 4.0, поставляемый в .NET 4.0, я получаю следующую ошибку для вашего первого примера:
ошибка CS0534: "ConsoleApplication3.Square" не реализует унаследованный абстрактный элемент "ConsoleApplication3.ShapesClass.Area()"
И следующее предупреждение для второго:
предупреждение CS0114: 'ConsoleApplication3.Square.Area()' скрывает унаследованный элемент 'ConsoleApplication3.ShapesClass.Area()'. Чтобы заставить текущий элемент переопределить эту реализацию, добавьте ключевое слово переопределения. В противном случае добавьте новое ключевое слово.
В первом случае это ошибка, потому что вы на самом деле не переопределяете базовый метод, а это означает, что для абстрактного метода в конкретном классе не существует реализации. Во втором случае это предупреждение, потому что код технически корректен, но компилятор подозревает, что это не то, что вы имели в виду. Это одна из причин, по которой обычно рекомендуется включить настройку компиляции "обрабатывать предупреждения как ошибки".
Поэтому я не могу воспроизвести ваше поведение, и поведение компилятора выглядит правильно для меня. Какую версию компилятора вы используете?
Ответ 2
Здесь ответ прямо из спецификации С#.
... скрытие доступного имени из унаследованная область вызывает предупреждение сообщили. В примере
class Base
{
public void F() {}
}
class Derived: Base
{
public void F() {} // Warning, hiding an inherited name
}
объявление F в Производных причинах предупреждение, о котором следует сообщать. Скрытие унаследованное имя конкретно не является ошибки, поскольку это исключало бы отдельная эволюция базовых классов. Например, вышеупомянутая ситуация может возникли потому, что позже версия Base вводила метод F это не было в предыдущем версия класса. Если бы вышеупомянутое ситуация была ошибкой, тогда любая изменение, внесенное в базовый класс в отдельная версия библиотеки классов может потенциально классы становятся недействительными. Предупреждение вызванное скрытием унаследованного имени, может быть устранены путем использования нового Модификатор:
class Base
{
public void F() {}
}
class Derived: Base
{
new public void F() {}
}
Новый модификатор указывает, что F в Derived является "новым", и что это действительно предназначалось для скрытия унаследованных член.
Ответ 3
Разница заключается в том, что абстрактный метод должен быть переопределен, но виртуальный не работает.
Это ошибка наследования абстрактного класса (в не-абстрактном классе) без реализации всех абстрактных членов, но вы получаете предупреждение только при наследовании от класса без указания override
или new
для виртуального метода.
Ответ 4
Я думаю, что в первую очередь вы получаете ошибку компилятора, потому что абстрактный метод скрыт и не реализован (так эффективно ваш класс ошибается, и было бы трудно понять, почему). Во втором случае вы получаете только предупреждение, потому что класс полезен.
Ответ 5
Versioning
На первый взгляд вы можете подумать, что скрытие метода не кажется особенно полезным. Есть ситуация
где вам может понадобиться использовать его. Допустим, вы хотите использовать класс с именем MotorVehicle
, который
был написан другим программистом, и вы хотите использовать этот класс для получения собственного класса. В дальнейшем,
позволяет также предположить, что вы хотите определить метод Accelerate()
в производном классе. Например:
public class Car : MotorVehicle
{
// define the Accelerate() method
public void Accelerate()
{
Console.WriteLine("In Car Accelerate() method");
Console.WriteLine(model + " accelerating");
}
}
Далее, давайте предположим, что другой программист позже модифицирует класс MotorVehicle и решает
добавить свой собственный метод virtual Accelerate()
:
public class MotorVehicle
{
// define the Accelerate() method
public virtual void Accelerate()
{
Console.WriteLine("In MotorVehicle Accelerate() method");
Console.WriteLine(model + " accelerating");
}
}
Добавление этого метода Accelerate()
другим программистом вызывает проблему:
Accelerate()
метод в вашем классе Car
скрывает унаследованный метод Accelerate()
, определенный в
их класс MotorVehicle
. Позже, когда вы приступите к компиляции своего класса Car
, это не ясно для компилятора
действительно ли вы намеревались использовать ваш метод, чтобы скрыть унаследованный метод. Из-за этого
компилятор сообщает следующее предупреждение, когда вы пытаетесь скомпилировать свой класс Car:
предупреждение CS0114: "Car.Accelerate() скрывает унаследованный элемент" MotorVehicle.Accelerate(). Чтобы текущий член переопределил этот реализации, добавьте ключевое слово переопределения. В противном случае добавьте новое ключевое слово.