этот факт уже отмечался нами ранее. Но пока мы знакомы с полями
и методами, на их примере и рассматриваем вопрос о замещении
членов при наследовании.
С формальной точки зрения ситуация достаточно простая. В производном
классе описывается, например, поле с таким же именем и типом, как поле
в базовом классе. Также это может быть метод с такими же атрибутами, включая имя и список аргументов. В этом случае класс получает два члена
с одинаковыми атрибутами. И это не является ошибкой. Единственное, что
нам следует указать, сознательно или нет мы допускаем такую ситуацию.
Если в производном классе мы специально описываем новый старый член, перед этим членом указывается ключевое слово new. Единственное назна-
чение идентификатора new в такой ситуации — показать, что мы в курсе
того, что у класса два одинаковых члена. Не больше.
Если инструкцию new возле члена-клона в производном классе не
указать, программный код будет откомпилирован, но с предупре-
ждением. Поэтому в известном смысле использование инструкции
new — это скорее правила хорошего тона, чем острая необходи-
мость.
Итак, допустим, что у нас есть класс, который создан путем наследования
на основе базового класса. Для производного класса описан такой же член, как и в базовом классе. Неприятность в том, что член базового класса на-
следуется. Получается, что в производном классе как бы два члена, и оба
они как бы один член. Возникает два вопроса: как все это понимать, и что
в такой ситуации делать?
Ответы достаточно простые и во многом возвращают кризисную ситуацию
в обычное русло. Во-первых, технически существует два члена. Во-вторых, по умолчанию, если выполняется обращение к такому двойному члену, обращение это выполняется на самом деле к тому, который явно описан
в производном классе. Этот член как бы заслоняет или замещает собой
член, наследуемый из базового класса. Вместе с тем второй (замещенный) член никуда не девается, просто доступ к нему скрыт. В программном коде
производного класса к замещенному члену из базового класса можно вы-
полнить обращение с помощью инструкции base, указав после нее через
точку имя соответствующего поля или заголовок метода. В качестве иллю-
страции рассмотрим пример из листинга 2.8.
Замещение членов класса и переопределение методов 87
Листинг 2.8. Замещение членов класса при наследовании
using System;
// Базовый класс с полем и методом:
class A{
// Открытое текстовое поле:
public string name;
// Конструктор класса с одним
// текстовым аргументом:
public A(string txtA){
name=txtA;
}
// Открытый метод для отображения значения поля:
public void show(){
Console.WriteLine("Класс А: "+name);
}
}
// Производный класс от класса A:
class B:A{
// Замещение текстового поля
// в производном классе:
new public string name;
// Конструктор производного класса
// с двумя аргументами:
public B(string txtA,string txtB):base(txtA){
name=txtB;
}
// Замещение метода в производном классе:
new public void show(){
Console.WriteLine("Класс B: "+name);
}
// Метод содержит ссылки на замещенные
// члены класса:
public void showAll(){
Console.WriteLine("Небольшая справка по объекту класса B.");
// Ссылка на поле name из базового класса:
Console.WriteLine("Поле name из класса A: "+base.name);
// Ссылка на поле name из производного класса:
Console.WriteLine("Поле name из класса B: "+name); Console.WriteLine("Вызов метода show() из класса A:");
// Вызов метода show() из базового класса:
base.show();
Console.WriteLine("Вызов метода show() из класса B:");
// Вызов метода show() из производного класса:
show();
88
Глава 2. Классы и объекты
Листинг 2.8 (продолжение)
// Переход к новой строке:
Console.WriteLine();
}
}
// Класс с главным методом программы:
class ABDemo{
// Главный метод программы:
public static void Main(){
// Объект производного класса:
B objB=new B("поле класса А","поле класса В");
// Вызов метода, в котором есть
// ссылки на замещенные члены:
objB.showAll();
// Объектная переменная базового класса:
A objA;
// Объектная переменная базового класса
// ссылается на объект производного класса:
objA=objB;
// Вызываем метод show() через объектную
// переменную производного класса:
objB.show();
// Вызываем метод show() через объектную
// переменную базового класса:
objA.show();
// Ожидание нажатия какой-нибудь клавиши:
Console.ReadKey();
}
}
У класса A есть текстовое поле name и show() для отображения значения
этого поля. Кроме значения поля name, методом show() также выводится
тестовое сообщение, которое позволяет однозначно определить, что метод
описан именно в классе A. Также у класса имеется конструктор с одним
аргументом, который определяет значение текстового поля name создавае-
мого объекта.
Во многом класс B дублирует класс A. Класс B создается наследованием
класса A. В классе B описывается поле name — такое же, как и то, что на-
следуется классом B из класса A. Поэтому в классе B при описании поля
name мы указали атрибут new. Еще в классе B описывается метод show().