Наследование и полиморфизм
Переопределение членов базового класса
При объявлении членов производного класса в C# разрешено использовать те же самые имена, которые применялись при объявлении членов базового класса. Это касается как методов, так и данных — членов объявляемых классов.
В этом случае соответствующие члены базового класса считаются переопределенными.
Следующий простой фрагмент кода содержит объявления базового и производного классов. Особое внимание в этом примере следует уделить ключевому слову new в объявлении одноименных членов в производном классе. В данном контексте слово new выступает в качестве модификатора, который используется для явного обозначения переопределяемых членов базового класса:
using System; namespace Inheritance_2 { class X { public X(){} public X(int key){} public int q0; public int q; public void fun0() { Console.WriteLine("class X, function fun0()"); q = 125; } public void fun1() { Console.WriteLine("class X, function fun1()"); } } class Y:X { public Y(int key){} public Y():base(125){} new public int q; // Если опустить модификатор new – new public void fun1() // появится предупреждение об ожидаемом new... { base.fun1(); // Обращение к переопределенным членам базового класса. base.q = 125; Console.WriteLine("class Y, function fun1()"); } static void Main(string[] args) { Y y0 = new Y(); Y y1 = new Y(125); // А извне переопределенные члены базового класса не видны. y0.fun1(); y0.q = 100; y0.fun0(); y0.q0 = 125; } } }Листинг 7.2.
При переопределении наследуемого члена его объявление в классе-наследнике должно предваряться модификатором new. Если этот модификатор в объявлении производного класса опустить – ничего страшного не произойдет. Транслятор всего лишь выдаст предупреждение об ожидаемом спецификаторе new в точке объявления переопределяемого члена класса.
Дело в том, что переопределенные члены базового класса при "взгляде" на производный класс "извне" не видны. В производном классе они замещаются переопределенными членами, и непосредственный доступ к этим членам возможен только из функций — членов базового и производного классов. При этом для обращения к переопределенному члену из метода производного класса используется ключевое слово base.
По мнению создателей языка C#, тот факт, что ранее (до момента его переопределения) видимый член (базового) класса стал недоступен и невидим извне (в результате его переопределения в производном классе), требует явного дополнительного подтверждения со стороны программиста.
Объявление класса может содержать вложенное (или внедренное) объявление класса. C# допускает и такие объявления:
class X { public class XX { } }
Одновременное включение явного объявления класса или структуры с одним и тем же именем в базовый и производный классы также требует явной спецификации с помощью модификатора new:
class X { public class XX { } } class Y:X { new public class XX { } }
Модификатор new используется при переопределении общедоступных объявлений и защищенных объявлений в производном классе для явного указания факта переопределения.
Наследование и new-модификатор
Этот модификатор используется при объявлении класса-наследника в том случае, если надо скрыть факт наследования объявляемого члена класса.
public class MyBaseC { public int x; public void Invoke() { ::::: } }
Объявление члена класса Invoke в наследуемом классе скрывает метод Invoke в базовом классе:
public class MyDerivedC : MyBaseC { new public void Invoke() { ::::: } }
А вот данное-член x не было скрыто в производном классе соответствующим членом, следовательно, остается доступным в производном классе.
Сокрытие имени члена базового класса в производном классе возможно в одной из следующих форм:
- константа, поле, свойство или внедренный в класс тип или структура скрывают ВСЕ одноименные члены базового класса;
- внедренный в класс или структуру метод скрывает в базовом классе свойства, поля, типы, обозначенные тем же самым идентификатором, а также одноименные методы с той же самой сигнатурой;
- объявляемые в производном классе индексаторы скрывают одноименные индексаторы в базовом классе с аналогичной сигнатурой.
Одновременное использование в производном классе члена базового класса и его переопределенного потомка является ошибкой.