Интересности      Книги      Утилиты    

25 января 2011 г.

C#: разница между new и override

Сказать по правде я долго над этим не задумывался. Нет, я знал что в C# существует модификатор new и что он скрывает реализацию членов базового класса, также он может менять модификаторы доступа (о чем я со временем забыл). Т.е. если в базовом классе, например, метод определен как публичный, используя модификатор new в наследнике – можно сделать его приватным.

Но на практике я никогда не использовал этот модификатор. Почему? Потому что его использование мне кажется говорит о том, что с кодом что-то не так. Для того чтобы реализовать полиморфизм достаточно и широко распространенных virtual-override. И если в классе потомке приходится скрывать, к примеру, реализацию базового класса. То возможно потомок наследуется не от того базового класса или базовый класс стоит раздробить на более специфицированные.

Как пример приведу два класса:

class Musician
{ 
 public void PlayPiano() 
 { 
  Console.WriteLine("Я - музыкант, я я играю на рояле");
 }
 
 public void PlayDrum() 
 { 
  Console.WriteLine("Я - музыкант, я играю на ударных");
 }
}

class Pianist : Musician
{
 new private void PlayDrum() { }

 new public void Play()  
 { 
  Console.WriteLine("Я - пианист, я играю но рояле");
 }
}

Пример, конечно, синтетический, но хотело продемонстрировать что использования new можно избежать. Достаточно выделить класс Musician как базовый сделать у него один метод Play и создать два потомка Drummer и Pianist, которые будут переопределять этот метод.

Собственно, почему я решил на писать о new и разнице его с virtual-override – потому что меня об этом спросили. И в силу того что практического использования этого модификатора не имел – забыл нюансы.

Основная разница между ними заключается в том как они ведут себя при полиморфизме. Итак пусть есть два класса - Музыкант, который играет на музыкальном инструменте, но неизвестно каком:

class Musician
{ 
 public virtual void Play() 
 { 
  Console.WriteLine("Я - музыкант, но я не знаю на чем я играю");
 }
}

И класс Пианист, потомок от класса Музыкант, который знает точно, что он играет на рояле:


class Pianist : Musician
{
 public override void Play()  
 { 
  Console.WriteLine("Я - музыкант, я играю но рояле");
 }
}

Итак вся разница между ними – это что выведется на консоль при вызове метода Play, а точнее какой метод из них будет вызван:

Musician player = new Pianist();
player.Play();

Итак в случае с virtual-override выведется:

Я - музыкант, я играю но рояле

Если же определить метод Play с использованием модификатора new на консоль выведется другое сообщение:

class Pianist : Musician
{
 new public void Play()  
 { 
  Console.WriteLine("Я - музыкант, я играю но рояле");
 }
}


Код выше выведет:

Я - музыкант, но я не знаю на чем я играю

При использовании new метод Play не перекрывает реализацию базового класса, а подменяет ее своей (по сути прячет ее). Поэтому когда мы сохраняем переменную типа Pianist в переменной базового типа Musician, мы не получим вызова override метода из класса Pianist , так как в не переопределяется, а  подменяется метод используя new. При использовании virtual-override происходит виртуальный вызов и будет вызван переопределённый (overriden) метод из класса Pianist.

Нашел по этому поводу хорошее обсуждение на stackoverflow. В одном из ответом даже приведена картинка для пояснения.  Вот она:

Знать о существовании модификатора new конечно нужно. Но его стоит использовать в исключительных случаях. Возможно, когда исходный код базовых классов недоступен. У меня складывается впечатление что вопрос про разницу new и virtual-override чаще используется на интервью в качестве каверзного вопроса чем на практике.

3 комментария:

  1. Кстати я совсем забыл написать - использование new нарушет один из принципов SOLID а именно - Liskov substitution principle.

    ОтветитьУдалить
  2. Модификатор New еще позволяет переопределить тип свойства или возвращаемого значения функции в классе-наследнике, в c# во всяком случае точно так)

    ОтветитьУдалить