Г. Шилдт - С#4.0 Полное руководство (1160795), страница 65
Текст из файла (страница 65)
Член 1 в базовом классе: 1 Член 1 в производном классе: 2 Несмотря на то что переменная экземпляра 1 в производном классе В скрывает переменную 1 из базового класса А, ключевое слово Ьаэе разрешает доступ к переменной 1, определенной в базовом классе. С помощью ключевого слова Ьаэе могут также вызываться скрытые методы. Например, в приведенном ниже коде класс В наследует класс А и в обоих классах объявляется метод ЗЬоы () . А затем в методе ЗЬоы () класса В с помощью ключевого слова Ьаэе вызывается вариант метода БЬои (), определенный в классе А.
346 Час(ь 1. язык С№ // Вызвать скрытый метод. пязпЧ Буягепы с1аяя А ( рнь11с 1пг 1 0) // Метод Бдсн() в классе А рпЪ11с чсьб Бьсн() ( Сспяс1е.иг1геъ1пе("Член 1 в базовом классе: " + 1) ) // Создать производный класс. с1ая* В : А ( пен ьпг 1) // этот член скрывает член 1 иэ класса А рпЬ11с В(ьпг а, зпс Ь) ( Ьаяе.з = а; // здесь обнаруживается скрытый член иэ класса А 1 = Ь; // член 1 из класса В // Здесь скрывается метод Бдсн() иэ класса А. Обратите // внимание на применение ключевого алова пен. пен рпнзьс чоаб Бьон() ( Ьаяе.зьсн(); // здесь вызывается метод Бьсн() иэ класса А // далее выводится член 1 из класса В Сопяс1е.нг1геъ1пе("Член 1 в производном классе: " т 1); ) ) с1аяя Опссчегиаюе ( ягагас чотб Маап() ( В оЬ = пен В(1, 2); сЬ.БЬсн()) Выполнение этого кода приводит к следующему результату.
Член 1 в базовом классе: 1 Член 1 в производном классе: 2 Как видите, в выражении Ьаяе. БЬоы () вызывается вариант метода БЬон () из базового класса. Обратите также внимание на следующее: ключевое слово пеы используется в приведенном выше коде с целью сообщить компилятору о том, что метод Бьон (), вновь объявляемый в производном классе В, намеренно скрывает другой метод БЬоы (), определенный в базовом классе А.
Создание многоуровневой иерархии классов В представленных до сих пор примерах программ использовались простые иерархии классов, состоявшие только из базового и производного классов. Но в О) мож- Глава 11. Наследование 347 но также строить иерархии, состоящие из любого числа уровней наследования. Как упоминалось выше, многоуровневая иерархия идеально подходит для использования одного производного класса в качестве базового для другого производного класса.
Так, если имеются три класса, А, В и С, то класс С может наследовать от класса В, а тот, в свою очередь, от класса А. В таком случае каждый производный класс наследует характерные особенности всех своих базовых классов. В частности, класс С наследует все члены классов В и А. Для того чтобы показать, насколько полезной может оказаться многоуровневая иерархия классов, рассмотрим следующий пример программы. В ней производный класс Тг1апп1е служит в качестве базового для создания другого производного класса — СО1огтг1апд1е.
При этом класс СО1огтг1апс1е наследует все характерные особенности, а по существу, члены классов Тг1апп1е и ТиоОЯЬаре, к которым добавляется поле со1ог, содержащее цвет треугольника. Пример построения многоуровневой иерархии классов. ов(по Яувг еш; с1авв ТноОЯЬаре бопЫе рг1 н1г(гпг г(опЫе рг1 Ьеьдпгг Конструктор, используемый по умолчанию.
рпЬ11с ТноОЯЬаре() ( Наг)ГЬ = Негопг = О.О? Конструктор лля класса Тно05Паре. рпЬ11с ТноОЯЬаре(бопЫе н, НооЫе Ь) ( Нгасп = н; Негдпг = Ь1 ) // Сконструировать объект равной ширины и высоты. рпЬ11с ТноО5Паре(бопЬ1е х) ( Нгзсп = Не1ЧЬГ = хг ) // Свойства ширины и высоты объекта.
рпЫТс г(опЬ1е Н1сгп ( дег ( гегогп рг1 н1огп( ) вес ( рг1 нг<)ГЬ = чагие < О ? -ча1пе: ча1пе( ) ) рпЬ11с бопЬ1е НегчЬ~ ( дес ( геспгп ргг Ье1ОЬГ( ) вес ( ргг Ье1опг = ча1пе < О ? -ча1ое : ча1пег ) рпЫТс чоха ЯпонР1ш() ( Сопво1е.нг1сеЬТпе("Ширина и высота равны Нгасп + " и " т Неьопс); ) 348 Часть ). Язык С№ // Класс для треугольников, производный от класса ТноОБЬаре.
с1аяя Тгьапд1е : ТноПЯЬаре ( яог1по Яоу1е; // закрытый член класса /* Конструктор, используемый по умолчанию. Автоматически вызывает конструктор, доступный по умолчанию в классе Тнорзьаре. */ рцЫТс Тгзап81е() ( Бсу1е = "пч11"; ) // Конструктор. роЫТс Тгьапо1е (ясг1по я, бооЫе н, СооЫе Ы: Ьаяе (н, Ы ( Боу1е = я) ) Сконструировать равнобедренный треугольник. \ рцЫТс Тг1апо1е (с(оцЫе х): Ьаяе (х) ( Яяу1е = "равнобедренный"; ) // Возвратить площадь треугольника.
рцЫьс с(оцЬ1е Агеа() ( гегцгп Хгс(ГЬ * НегдЬГ / 2; ) Показать тип треугольника. рцЫТс чогс) БЬонзгу1е() ( Сопяо1е.кг1сеььпе("Треугольник " + ясу1е) ) ) // Расширить класс Тгьап81е. с1аяя Со1огтг1апо1е : Тг1ап81е ( яогьпо со1ог; рцЫ1с Со1огТг1ап81е(ясгьпд с, ясгьпр я, с)оцЬ1е н, НоцЬ1е Ь): Ьаяе(я, н, Ы ( со1ог = с) ) // Показать цвет треугольника. риЬ11с чоьб БьонСо1ог() ( Сопяо1е.кг1се11пе("Цвет " + со1ог)) ) с1аяя БЬареяб ( ягаг1с чо1с( маап() ( Со1огТгьап81е Г1 = пен Со1огтг1апп1е("синий", "прямоугольный", Б.О, 12.0); Со1огТгзап81е Г2 = Глава 11.
Наследование 349 пен Сс1сгтгтапд1е("красный", "равнобедренный", 2.0, 2.0)у Сспас1е.нгьгегипе("Сведения сб объекте Г1: "); 11. Бнснзгу1е (); Г1.БПснбьщ(); С1.БПснсс1сг(); Сспзо1е.нгьгеъьпе ("Площадь равна " + Г1 .Лгеа () ); Сспас1е.Хгьсеэьпе() Сспас1е.нгьсеълпе("Сведения сб объекте Г2: "); 12.8пснзсу1е(): Г2.БПснпьщ(): Г2.БПснСс1сг(); сспас1е.хг1гешпе("плсщадь равна " + г2.лгеа()); ) ) При выполнении этой программы получается следующтГй результат.
Сведения сб объекте Г1: Треугольник прямоугольный Ширина и высота равны 8 и 12 Цвет синий Площадь равна 48 Сведения сб объекте Г2: Треугольник равнобедренный Ширина и высота равны 2 и 2 Цвет красный Площадь равна 2 Благодаря наследованию в классе Со1огТг1апц1е могут использоваться определенные ранее классы Тгьапо1е и ТхоРБЬаре, к элементам которых добавляется лишь та информация, которая требуется для конкретного применения данного класса.
В этом отчасти и состоит ценность наследования, поскольку оно допускает повторное использование кода. Приведенный выше пример демонстрирует еще одно важное положение; ключевое слово Ьазе всегда обозначает ссылку на конструктор ближайшего по иерархии базового класса. Так, ключевое слово Ьазе в классе Со1огтг1апс1е обозначает вызов конструктора из класса Тг1апс1е, а ключевое слово Ьазе в классе Тг1апц1е — вызов конструктора из класса ТноПБЬаре. Если же в иерархии классов конструктору базового класса требуются параметры, то все производные классы должны предоставлять эти параметры вверх по иерархии, независимо от того, требуются они самому производному классу или нет.
Порядок вызова конструкторов В связи с изложенными выше в отношении наследования и иерархии классов может возникнуть следующий резонный вопрос: когда создается объект производного класса и какой конструктор выполняется первым — тот, что определен в производном классе, или же тот, что определен в базовом классе? Так, если имеется базовый класс А 350 Часть!. Язык С№ и производный класс В, то вызывается ли конструктор класса А раньше конструктора класса В? Ответ на этот вопрос состоит в том, что в иерархии классов конструкторы вызываются по порядку выведения классов: от базового к производному. Более того, этот порядок остается неизменным независимо от использования ключевого слова Ьаяе.
Так, если ключевое слово Ьа яе не используется, то выполняется конструктор по умолчанию, т.е, конструктор без параметров. В приведенном ниже примере программы демонстрируется порядок вызова и выполнения конструкторов. // Продемонстрировать порядок вызова конструкторов. пя1по Зуягеиг // Создать базовый класс. с1аяя А ( роЬ1гс А() ( Сопяо1е.нгггеь1пе("Конструирование класса А.") /! Создать класс, производный от класса А.
с1аяя В : А ( роЬ11с В() ( Сопяо1е.иг1сеШпе("Конструирование класса В.")Г ) // Создать класс, производный от класса В. с1аяя С : В ( рпЬ1гс С() Сопяо1е.нгтгеь1пе("Конструирование класса С.") ) с1аяя Огбегбтсопяггост1оп ( ягагьс ко1с) Маьп () ( С с = пен С(); ) Вот к какому результату приводит выполнение этой программы. Конструирование класса А. Конструирование класса В. Конструирование класса С. Как видите, конструкторы вызываются по порядку выведения их классов.
Если хорошенько подумать, то в вызове конструкторов по порядку выведения их классов можно обнаружить определенный смысл. Ведь базовому классу ничего не известно ни об одном из производных от него классов, и поэтому любая инициализация, которая требуется его членам, осуществляется совершенно отдельно ат инициализации членов производного класса, а возможно, это и необходимое условие. Следовательно, она должна выполняться первой.