Полиморфизм модификаторы new и virtual/override

161
05 октября 2018, 19:30

Подскажите, пожалуйста, почему вызов метода с модификатором new не является полиморфным, а с модификаторами virtual/override является?

Answer 1

Вопрос на понимание теории, поэтому постараюсь ответить просто, но понятно.

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

В C# принцип полиморфизма реализуется с помощью виртуальных методов и переопределения (override) их в наследниках.

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

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

Возьмем для примера птиц, все птицы имеют название и могут перемещаться в пространстве, поэтому мы можем создать такой базовый класс:

abstract class Bird
{
    public virtual string Name => "Птица";
    public void Move() { Console.WriteLine($"{Name} летит"); }
}

Свойство Name сразу сделали виртуальным, ведь название то у каждой птички свое, а метод Move оставили обычным, большинство ведь летает, а у исключений перекроем потом, делов-то.

Теперь мы хотим разнообразить наш зоопарк и заводим производные классы переопределив свойство Name

class Eagle : Bird 
{
    public override string Name => "Орёл"; 
}
class Ostrich : Bird
{
    public override string Name => "Страус"; 
    //и тут вспоминаем, что страус не летает, а бегает
    new void Move() { Console.WriteLine($"{Name} бежит"); }
}
class Penguin : Bird 
{
    public override string Name => "Пингвин";
    //а пингвин и бегает с трудом, зато плавает
    new void Move() { Console.WriteLine($"{Name} плывет"); }
}

Вроде все хорошо, все учли. Собираем зоопарк в кучу:

Bird[] birds = {new Eagle(), new Ostrich(), new Penguin()};

И выводим на прогулку:

foreach(Bird b in birds)
{
    b.Move();
}

и выясняем интересный факт, с именами все нормально, но внезапно все научились летать:

Вывод в консоль в результате выполнения цикла:
Орёл летит
Страус летит
Пингвин летит

Name у нас было виртуальным, поэтому работает полиморфизм и несмотря на то, что переменная у нас базового класса Bird, получаем то, что хотели получить - у каждой птички свое имя.

Move не был виртуальным, мы просто скрыли его базовую реализацию под новым методом класса-наследника. В этом случае полиморфизм не работает и при обращении через переменную базового класса вызывается базовая реализация метода. Поэтому сами по себе страусы у нас как положено бегают, пингвины плавают, а в компании с другими птицами могут и полетать, даже если крылья вообще не предусмотрены природой, как у Киви. =)

READ ALSO
Не обновляется datagridview при очистке базы данных sql

Не обновляется datagridview при очистке базы данных sql

Излазил уже просторы интернета, но до сих пор не понял как правильно обновить datagridview Что делаю не так?

158
Использование UserControl в ListBoxItem

Использование UserControl в ListBoxItem

подскажите пожалуйста, ради эксперимента в качестве Item ListBox использую вместо шаблона UserControl который связан в словаре ресурсов с моделью представления...

174
Сохранение ключей в базе данных

Сохранение ключей в базе данных

Хочу сохранять пароли в базе с помощью RSAЯ так понял по примеру - https://msdn

172