класса. Таким образом, получаем цепочку наследования: класс А является
базовым для класса В, а класс В является базовым для класса С. Это пример
множественного) наследования, в С# разрешено и широко используется
на практике.
Многократное наследование — это наследование, при котором один
класс создается сразу на основе нескольких базовых классов. Так де-
лать в C# нельзя. Многоуровневое наследование — это наследование, при котором производный класс сам является базовым для другого
класса. Так в C# делать можно. Этим мы и воспользовались выше.
84
Глава 2. Классы и объекты
В главном методе программы мы объявляем три объектные переменные: переменная objA класса A, переменная objB класса B и объектная перемен-
ная objC класса C. Причем последней в качестве значения присваивается
ссылка на новосозданный объект класса C. И пока все банально. Неба-
нально становится, когда мы командами objA=objC и objB=objC ссылку на
объект класса C присваиваем объектным переменным objA и objB. После
этого все три переменные (objA, objB и objC) ссылаются на один и тот же
объект.
О том, что переменная базового класса может ссылаться на объект
производного класса, мы уже намекали ранее. В этом смысле присваи-
вание переменной класса B ссылки на объект класса С не является
неожиданностью. Но, поскольку класс B является производным от
класса A, то на объект класса C может ссылаться и переменная класса
A. Имеет место своеобразная транзитивность. При этом ограничение
остается прежним: доступ через объектную переменную есть только
к тем членам, которые прописаны в классе, к которому относится
объектная переменная.
Однако полномочия у переменных objA, objB и objC разные. Переменная
objC имеет доступ ко всем трем полям и методам. Переменная objB имеет
доступ к двум полям и двум методам: тем, что описаны в классе B и унасле-
дованы в классе B из класса A. Через переменную objA доступны только те
поля и методы, которые описаны непосредственно в классе A.
Для разнообразия мы вместо метода Console.ReadLine() в главном
методе программы использовали метод Consile.ReadKey(). Метод
Console.ReadLine() считывает текст ввода в консоли, а признаком
окончания ввода является нажатие клавиши Enter. Метод Consile.
ReadKey() считывает нажатую клавишу. Поэтому в рассматриваемом
примере консольное окно не закроется, пока мы не нажмем какую-
нибудь клавишу. Если бы мы использовали метод Console.ReadLine(), пришлось бы нажимать именно клавишу Enter.
Командами objB.nameA="красный" и objB.nameB="желтый" через перемен-
ную objB заполняем поля объекта objC. Третье поле, nameC, через пере-
менную objB недоступно. Поэтому, чтобы присвоить полю значение, ис-
пользуем команду objC.nameC="зеленый". Но перед этим командами objB.
showA() и objB.showB() проверяем поля, у которых есть значения. К тре-
тьему, незаполненному полю эти методы не обращаются. После того как
заполнено и третье поле, проверяем результат присваивания значения
Замещение членов класса и переопределение методов 85
полям с помощью команды objC.showC(). Если же мы хотим получить до-
ступ к объекту класса C через переменную класса A, то доступными будут
лишь поле nameA и метод showA() объекта класса C. Эту ситуацию иллю-
стрируют команды objA.nameA="белый" и objA.showA(). Результат выполне-
ния программы пред ставлен на рис. 2.5.
Рис. 2.5. Объектные переменные и наследование: результат выполнения программы
Как мы увидим далее в книге, не только объектные переменные базового
класса имеют честь ссылаться на объекты производных классов. В C# есть
терфейсного типа могут ссылаться на объекты классов, в которых реали-
зуется соответствующий интерфейс. Ситуация во многом схожа с объект-
ными переменными базовых типов. Вместе с тем имеются и существенные
различия, но их обсуждать сейчас не время.
Замещение членов класса
и переопределение методов
— Так что же, выходит, у вас два мужа?
— Выходит, два.
— И оба Бунши?
— Оба!
С наследованием связано еще два выдающихся феномена — замещение
членов и переопределение виртуальных методов. В некотором смысле они
идеологически близки, поскольку в обоих случаях речь идет о том, что
имеет место конфликт (в хорошем смысле этого слова) между унаследо-
ванным из базового класса членом и аналогичным членом, описываемым
в производном классе. Начнем с замещения. Суть его состоит в том, что
при наследовании в производном классе описывается член с абсолютно
такими же параметрами, как и в базовом классе. Это может быть как поле, так и метод.
86
Глава 2. Классы и объекты
ПРИМЕЧАНИЕ Строго говоря, полями и методами члены класса не ограничиваются.
Членами класса могут быть, например, свойства или индексаторы —