Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 41
Текст из файла (страница 41)
Данный интерфейс определяет контракт, который устанавливает, что любой тип, реализующий этот интерфейс, должен реализовать метод Ра1пС. Конечно, весьма желательно наличие некоторой документации, описывающей семантическое значение того, что должен делать Разпг. Например, интерфейс по имени 1Аггзяг может иметь метод Раапг, но его смысл будет совершенно другим. чем в предыдущем примере, те. 1О1Сопгго1. Разпщ скорее всего, предложит элементу управления нарисовать себя, тогда как 1Аггзяп.
Рауля, вероятно, означает. что художник должен рисовать что-нибудь. На заметку! Обнаружилось, что методы удобно называть в соответствие как с действием, которое они выполняют так и с направлением этого действия. Например, предположим, что метод 1В1сопсго1. Разия принимает в качестве параметра объект агарьзся, сообщающий ему, где он должен рисовать себя. По идее, код будет более читабельным, если метод назвать 1О1сопсго1. Ра1псБе11то.
Таким образом, вызов метода читается как предложение на естественном (английском) языке — в том смысле, что вызов метода вроде сопсго1. Раапсзе11то (иуОгарьусяОьбесс) сообщает сопсго1 о необходимости нарисовать себя на иупгарьасяОь3 есс. Интерфейсы и контракты 151 Как только классы 11 э свох и Вцс с оп из предыдущего примера реализуют интерфейс, они оба могут трактоваться как относящиеся к типу 101сопсго1. Полезно рассмотреть, как среда СЬК справляется с этой ситуацией. Попытка сохранения любого экземпляра ьазсВох или Вцгсоп в переменной типа 101сопсго1 завершится успешно.
Ссылки на эти конкретные типы неявно преобразуемы в интерфейсный тип 101Сопгго1, поскольку оба реализуют этот интерфейс. Однако чтобы привести ссылку на 1!!1Сопгго1 обратно к ь1эсвох или Вотсоп, потребуется явное приведение, и это приведение может потерпеть неудачу во время выполнения, если на самом деле ссылка 101Сопгго1 не будет указывать на экземпляр нужного конкретного типа. Определение интерфейсов В предыдущем разделе вы получили представление о том, на что похожи объявления интерфейсов СВ.
Они выглядят подобно объявлениям классов, где ключевое слово с1аээ заменено 1псеггасе, а методы не имеют тела. Однако обратите внимание еще на ряд важных моментов. Если вы следуете рекомендованным соглашениям, то имена интерфейсов должны начинаться с буквы 1. Таким образом, интерфейсные типы очень легко обнаружить в коде. Интерфейсы могут иметь присоединенные к ним модификаторы доступа. Они указывают на то, видимо ли объявление интерфейса вне сборки. Поскольку большинство интерфейсов представляют собой контракты взаимодействия между поставщиками и потребителями, интерфейсы обычно объявляются как рцЬ11с, Члены интерфейса не могут иметь никаких модификаторов доступа. Однако они могут быть декорированы модификатором пеи, о котором речь пойдет ниже.
Члены интерфейсов всегда неявно общедоступны (рцЬ11с). Какой может быть смысл в не общедоступных членах интерфейса, если назначение интерфейса состоит в том, чтобы позволить двум объектам взаимодействовать друг с другом? Интерфейсы определяют контракты Чтобы подчеркнуть, что интерфейс только специфицирует контракт, следует привести аналогию между объявлением интерфейса и языками 10Ь ((п!ег(асе Оезсг1р(юп (лпроайе — язык описания интерфейсов) и УУЕОЬ (УУеЬ Зе!ч1сез Оезспрбоп (апйцаре — язык описания веб-служб).
И СОМ, и СОЯВА используют 100 для определения интерфейсов. Его синтаксис подобен С+ч. Обычно 10Ь передается через транслятор, такой как маг(1. ехе для СОМ, чтобы генерировать оболочки и, возможно, п рокс и и заглушки для любого необходимого языка. Другим примером может служить УУВОЬ, который является гораздо более выразительным, чем!00 Схема ХМЬ определяет формат УУВО~, а документ УУЗОЬ применяется для описания контракта, или интерфейса, сетевой службы. Порядок использования подобен 100 Документ УУЗОЬ прогоняется через транслятор для любого языка, на котором осуществляется реализация или потребление службы.
Транслятор помогает тем, что генерирует оболочку реализации, или интерфейсы в виде, понятном для используемого языка. Объявление и потребление интерфейсов в среде,МЕТ должно следовать одному и тому же шаблону. На практике обычно имеет смысл помещать объявления интерфейсов в отдельную сборку, содержащую только определения интерфейсов и константы, чтобы потребитель и поставщик могли основывать свои реализации в точности на одной и той же версии интерфейса.
Замечательным примером этого может служить библиотека Мападеб Абб!и Евглеаогх (МАЕ), которая появилась в .МЕТ З.б. 152 Глава 5 Что может быть интерфейсом? Объявления интерфейсов могут включать ноль или более методов, свойств, событий и индексаторов. Все они неявно общедоступны и не могут быть статическими. Интерфейсы могут наследоваться от одного и более других интерфейсов. Синтаксис точно такой же, как при наследовании класса. Когда речь идет об интерфейсах, то предпочтительнее считать, что если интерфейс В наследуется от интерфейса А, это значит, что если реализован интерфейс В, то также должен быть реализован и интерфейс А. Наследование классов просто подразумевает отношение "является" 1)э-а), где базовая реализация также наследуется.
Хотя при наследовании интерфейсов применяется тот же синтаксис, что и при наследовании классов, было бы неправильно рассматривать их как одно и то же, поскольку наследование интерфейсов объявляет обобщение, а никакой реализации при этом не наследуется. Таким образом, всякий раз, когда речь идет о наследовании инпюрфейса, следует думать об этом в терминах отношения "реплпзует". Это станет яснее, когда будет показано. каким образом производный класс может заново реализовать интерфейсы и как компилятор осуществляет отображение реализации интерфейса на конкретные типы, реализующие интерфейс. Ниже приведен пример объявления интерфейса: рпЬ11с Се1еозге чо1с) СВЕчепш 1МупзгаЬаве веппег ): рпЬ11с Епгеггзсе 1МупагзЬзвв: 1зегга11заЬ1е, 1Е1вроваЫе чоьо 1пвегш оЬ~есг е1етепг ); 1пг сопле ) оес) ) оЬ)всг СЫв) 1пг Епбех ] ( дее! эес; ) ечепг ЬВЕчепс с)ЬСЬапдев; В данном примере 1МупзсаЬаве также реализует 1Бег1а11хвЫе и 101вровзЫе, Поэтому любой конкретный тип, реализующий 1му0асаЬаве, также должен реализовать ЕБег1а11звЫе и 1В1вроваЫе; в противном случае этот конкретный тип не скомпилируется.
Если после компиляции приведенного фрагмента кода заглянуть в полученную сборку с помощью! ЬЕ)АЗМ, можно заметить, что тип 1МупзгаЬаве не содержит ничего помимо объявлений методов. Конечно, некоторые из их будут иметь специальные имена, основанные на том факте, что они являются средствами доступа к свойству, нндексатором или событием. Наследование интерфейсов и сокрытие членов Как упоминалось ранее, интерфейсы поддерживают множественное наследование от других интерфейсов в синтаксическом смысле. Как и с множественным наследованием в С++, можно также строить ромбовидные решеточные иерархии, что показано в следующем коде; рпЬ11с Рпгвг1асе 101Сопгго1 ( чеза Рагпс)); ) рпЫЕс Епгеггвсе 1Ес)1СВох : 101Сопгго1 ) ) ' Не беспокойтесь, если не знакомы с ключевым словом с1е1едагв, и тем. как делегатЫ используются для объявления событий.
Исчерпывающее описание делегатов и событий дается в главе 10. Интерфейсы и контракты 153 рцвтпс 1ппегпясе 1огор11яп: 1С1Соппго1 ( ) рцЬ11с с1аяя СоюЬоВох: 1Ед1ГВох, 1Ргорггяп ( рцЬ11с то1д Рагпп() ( УУ рисовать Реализацию СоюЬЬВох ) В этом примере оба интерфейса 1Ед№ГВох и 1Ргорт Ряс реализуют интерфейс 1Р1Соппго1. И поскольку СоюЬовох реализует оба эти интерфейса, он должен реализовать объединение всех методов, объявленных в интерфейсах, которые он реализует непосредственно, плюс методы тех интерфейсов, от которых они наследуются, и т.д. В данном случае сюда относится только метод 101сопгго1. Ра1пг. Все довольно просто; методы из всех интерфейсов объединяются вместе в одно большое объединение, формируя набор методов, который должен реализовать конкретный класс или структура.
Поэтому класс СоюЬоВох получает только одну реализацию метода Ра1пп. Если выполнить приведение экземпляра Соп1ЬоВох к ссылке 1Ед1ГВох и ссылке 1дгор11яп, то вызов Ра1пп через любую из них приведет к вызову одной и той же реализации метода. На заметку! Если вы пришли из мира "родного" С++, то вам должна быть знакома вся сложность множественного наследования и диаграмм ромбовидных решеточных иерархий наследования, а также их связь с виртуальным наследованием в С++ и множеством генерируемых компилятором таблиц виртуальных методов. Чтобы понять, в чем состоит отличие С№, представьте, что С№ сливает все зти виртуальные таблицы во время компиляции в одну большую таблицу.
Иногда — хотя и крайне редко — в интерфейсе требуется объявить метод, который скрывает метод унаследованного интерфейса. Чтобы предотвратить при этом выдачу предупреждения компилятором, можно воспользоваться ключевым словом пен. На заметку! Традиционно объектно-ориентированный анализ и проектирование (ОСА-О) считает примером плохого дизайна сокрытие невиртуальных унаследованных членов. Реализация, которая в действительности вызывается, зависит от типа ссылки, даже если две ссылки указывают на один и тот же экземпляр. например, если А . Роиогк — невиртуальный метод, и В наследуется от А и вводит новый метод В . Роиогк, который скрывает базовый, то вызов Рохогк на ссылке типа В вызовет В. Рохогк а вызов того же метода на ссылке типа А, полученной приведением ссылки на в к типу А, вызовет А.















