Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 23
Текст из файла (страница 23)
ОояовеппьпдЕ1яе (); Здесь в Маьп создается новый экземпляр класса В, унаследованного от класса й. Класс В получает объединение членов обоих классов — й и В. Вот почему можно вызывать и ОояоглеСЬ1пд, и Оояовегп1пдЕ1яе на экземпляре класса В. Это вполне очевидно, поскольку наследование расширяет функциональность. Но что, если необходимо наследовать от класса й,но скрыть метод ОоЯояеспьпд'? Другими словами, что если требуется расширить лишь часть функциональности й'? )(лесов, структуры и обьекты 83 С помощью наслсдования это невозможно. Однако есть возможность сокрьюптя члена, как показано в следующем коде, который является модифицированной формой предыдущего примера: рпЬ11с с1аяя А ( рпЬ11с чотб Оозовепбьпд() ( Яуягев.Сопяо1е.игтке11пе( "А.ОоЯовегптпс" ) ) риЬ11с с1аяя В: А ( рпЬ11с чотс ОоэовегбтпдЕ1яе() ( Яуяпев.Сопяо1е.нг1Сеьтпе( "В.ОозовеСЬ1пдЕ1яе" ); ) рпвттс печ чоаб ОЬЯоветбвпс() ( Буяпев.сопяо1е.нг1те11пе( "В.Оозовегбтпд" )> ) ) рпЬ11с с1аяя Епкгуготпт ( ягаттс чокб Матс() ( В Ь = печ В(); Ь.ОозовеСП1пд О ) ъ.пояовесп1пяе1яе()т А а = Ьт а.позовесп1пс()т ) ) Как видите, в этой версии в класс В введен новый метод по имени Оо Бове сь1 по.
Также обратите внимание на добавление ключевого слова пев к объявлению В. Оояовесй Впав. Если не добавить это ключевое слово. то компилятор выдаст предупреждение. Это его способ сообщить о необходимости выражаться яснее относительно сокрытия метода базового класса. Возможно, компилятор делает так потому, что подобное сокрытие членов обычно трактуется как плохой дизайн. Давайте разберемся, почему. Вывод предыдущего кода выглядит следующим образом: В.ОозовеСПЕпд В.ОоэовеСПтпдЕ1яе А.ОЬЯовегбьпд Первое, на что следует обратить внимание: то, какой именно метод ОояовесЬ1пд будет вызван, зависит от типа ссылки, через которую он будет вызван.
Это достаточно не очевидно, поскольку В является А, а вы знаете, что наследование моделирует отношение "является". В данном случае должен ли весь общедоступный интерфейс А быть доступным потребителям экземпляра класса Вт Если кратко, то нет. Когда действительно необходимо, чтобы метод вел себя по-разному в подклассах, тогда в точке определения класса А метод ОЬБовеСЬ Вод должен быть объявлен виртуальным.
При таком подходе правильнее было бы воспользоваться полиморфизмом. В этом случае должна вызываться самая последняя версия (версия наследника) метода Оояовесь1по, независимо от того, через какой тип ссылки он был вызван. 84 Глава 4 Позже мы еще вернемся к теме виртуальных методов, но давайте еще раз подумаем о них здесь. Перед тем как объявлять ВоБопесп1по виртуальным методом, необходимо принять во внимание и будущее его определение.
То есть следует предвидеть, что кто- нибудь может переопределить его функциональность. Это только одна причина того, почему наследование может усложниться в процессе проектирования по сравнению с тем, что кажется поначалу. Как только наследование задействовано, придется задуматься об очень многих подобных вещах. Но, как известно, никто не в состоянии предсказать будущее. Несмотря на то что класс В теперь скрывает реализацию ВЬБопеспапд класса А, помните, что он не удаляет ее. Он скрывает ее при вызове этого метода через ссылку типа В на объект.
Однако в методе Иа1п это легко обойти, применив неявное преобразование ссылки на экземпляр типа В в ссылку на экземпляр типа А с последующим вызовом через нее реализации А. ВоБотеппапд. То есть реализация А. ВЬБопеЬЬ1по не исчезла, она просто скрыта. И чтобы добраться до нее, понадобится просто проделать немного больше работы.
Предположим, что передается ссылка на экземпляр В методу, принимающему ссылку на экземпляр А, подобно тому, как зто делается в примере с ВгазЯЬаре. Ссылка на экземпляр В должна быть неявно преобразована в ссылку на экземпляр А, и если за этим последует вызов метод Возопепйапд этого экземпляра А, то получится А. ВоБопеСЬ1пд вместо В.
Ботетйапд. Наверное, это не то, чего ожидал тот, кто вызвал данный метод. Это — классический пример того, что если язык позволяет делать нечто подобное, зто не означает, что поступая так, вы создадите хороший дизайн. Почти любые существующие языки, включая С++, обладают такими средствами, которые. будучи использованными (у тому же неправильно), лишь привнесут излишнюю сложность и ухудшат дизайн.
Ключевое слово ьаве При наследовании класса в методе производного класса часто возникает необходимость вызова метода либо доступа к полю, свойству или индексатору базового класса. Для этой цели предусмотрено ключевое слово Ьазе. Это ключевое слово можно применять подобно любой другой переменной экземпляра, но его можно использовать только внутри блока конструктора экземпляра, метода экземпляра или средства доступа к свойству. Применение его в статических методах не допускается. Это совершенно оправдано, потому что Ьазе открывает доступ к реализациям экземпляра базового класса, подобно тому, как ЬЬ1з разрешает доступ к экземпляру — владельцу текущего метода.
Рассмотрим следующий блок кода: рпЬ11с с1азз А ( рпЬ11с А( гпс чаг ) ( Сказ.х = чэг; рпЬ11с чагппа1 гоаб ПозотеГЬ1пч() ( Яузсеп.сопзо1е.кг1Ге11пе( "А.оозотеГЬ1пч" рг1часе апг х; ) РпЬ11с с1азз В: А рпЬ1гс В() : Ьазе( 123 ) ( Классы, структуры и обьекты 88 рипткс очеггьяе гоья Оояовесп1пд() ( Бузкев.оопзо1е .Хг1кет кое ( "В.
ОоЯовеСП1пд" Ьазе.рояовеСП1пд О ~ ) рпЬ11с с1азз япсгугоьпс зкат1с тоас Ма1п() ( В Ь = пеи В() т Ь.ОояовеСПапд(); В этом примере продемонстрированы два применения ключевого слова Ьазе, и одно из них — в конструкторе класса в. Напомним, что класс не наследует конструкторы экземпляра. Однако при инициализации объекта иногда требуется явно вызвать один из конструкторов базового класса во время инициализации производного класса.
Это объясняет нотацию, примененную в конструкторе экземпляра класса в. Инициализация базового класса происходит после обьявлення списка параметров конструктора производного класса, но перед блоком кода конструктора производного класса. Порядок вызовов конструкторов и инициализация объектов подробно рассматриваются в разделе "Создание объектов" далее в главе. Второе применение ключевого слова Ьазе содержится в реализации В. ОояовеСЬ1пд, Было принято решение, что при реализации метода ОояовеСЬ1пд в классе В необходимо позаимствовать реализацию Оояовегйяпд из класса А. Реализацию А. РояовеСЬ1пд можно вызвать непосредственно из реализации В.
Оояовегпяпд, снабдив вызов префиксом — ключевым словом Ьа з е. Если вы знакомы с виртуальными методами, то здесь, возможно, недоуменно пожмете плечами. Если метод Оояовеакяпд виртуальный, а ключевое слово Ьазе ведет себя нан переменная экземпляра базового класса, не случится ли так, что Ьаее. Рояовегйяпд на самом деле превратится в вызов В. ОояотлеСЬ).пд? В конце концов, так работает полиморфизм, и Ьззе. Ро5овекйяпд — эквивалент вызова ( (В) СЬ1з) .
РояовеСЬ1пд, что является просто приведением ссылки Спал к ссылке на класс В, и потому получится вызов В . Роя овеСЬ1пд, не правда лиг да. если бы это было так, то код в В. Ро Бове СЬВпд стал бы причиной бесконечного цикла. На самом деле никакого бесконечного цикла не будет. Когда ключевое слово Ьазе встречается внутри члена экземпляра при вызове виртуального метода, оно трактуется специальным образом. Обычно вызов виртуального метода на экземпляре означает вызов его наиболее "унаследованной" версии — в данном случае В. Боветп1пд.
Однако когда он вызывается через ключевое слово Ьззе, то вызывается наиболее "унаследованная" реализация метода базового класса. Поскольку А — базовый класс, а А.оояовекйяпд — наиболее унаследованнаяверсия метода Оояовегйяпд класса А, значит, Ьазе.Оояоветыспд вызывает А.оояовегп1пд. Подобным образом можно реализовать переопределенный метод, заимствуя реализацию базового класса.
Если вас интересуют подробности, то знайте, что в сгенерированном коде (Ь вызов через ссылку Ьазе происходит с использованием инструкции са11, а не са11чягг. Герметизированные классы Ранее уже упоминалось, что наследование — это такой мощный инструмент, которым легко злоупотребить. Фактически это настолько верно, что теме подводных камней, связанных с наследованием, посвящен целый раздел "Наследование, включение н деле- 86 Глава 4 гирование" настоящей главы.
Иногда имеются намерения сделать создаваемый новый класс базовым классом — заготовкой для дальнейшей специализации. Однако часто бывает и так, что классы проектируются, не принимая во внимание то, будут они использоваться в качестве базовых или нет. На самом деле весьма вероятно, что класс, который проектируется сегодня, будет использован в качестве базового завтра, даже если зто не предполагалось изначально.











