Г. Шилдт - С# 3.0 Полное руководство. 2010 (1160798), страница 104
Текст из файла (страница 104)
Несмотря на то что в программе из предыдущего примера класс мус1азз был полноценно использован без явного указания на его имя в программе, этот пример все же опирается на предварительную осведомленность о содержимом класса МуС1азз. Так, в программе были заранее известны имена методов Нес и Нпм из этого класса.
Но с помощью рефлексии можно воспользоваться типом данных, ничего не зная о нем заранее. С этой целью придется извлечь все сведения, необходимые для конструирования объекта и формирования вызовов соответствующих методов. Такой подход может оказаться пригодным, например, при создании инструментального средства визуального проектирования, поскольку он позволяет использовать типы данных, имеющиеся в системе. Рассмотрим следующий пример, демонстрирующий полностью автоматизированное обнаружение типа.
В этом примере сначала загружается сборка Мус1аззез.ехе, затем конструируется объект класса мус1азз и далее вызываются все методы, объявленные в классе МуС1азз, причем о них ничего заранее неизвестно. // Использовать класа МуС1азз, ничего не зная о неи // заранее. из1пз Зузгегп пз1пд Зузкеи.кек1еск1опк с1азз Нек1есГАвзееЬ1увезо ( зсак1с чо1к) Магп() ( 1пг ча1) АвзезЬ1у азы = АззезЬ1у.ъоак)угон("Мус1аззез.ехе")к Глава !7. Динамическая идентификация типов, рефлексия и атрибуты 543 Туре[] а11курез = аяи.деГТурез() Туре Г = а11сурея[0]т // испольэовать первый обнаруженный класс Сопяо1е.ыгяге11пе("Использовано: " + Г.ыаюе)т Сопясгцссогтпко[] с1 = С.дессопясгцссогя() // Использовать первый обнаруженный конструктор.
Рагаюесег1пео [! ср1 = са [О ) . Беграгатегегз () т оьбесг гее1есгоьт 1Е(ср1.1епдГЬ > 0) ( оЬбесс() сопяагдя = пен оЬ)есс[ср1.Ьепдсп] // Инициализировать аргументы, еог(1пг и От и < ср1.пепдгь) и++) сопяагдя[п] = 10 т и * 20т // Сконструировать объект. геЕ1есГОЬ = с1[0!.1пчоне(сопяагдя)т ) е1яе геЕ1есГОЬ = с1[0!.1пчоне(пц11)т сопяо1е.иг1ге11пе("1пиыэов методов для объекта гее1есгбь.") Сопяо1е.мгасе11пе(); // Игнорировать наследуемые методы.
Меспоб1пЕо[) ю1 Г.десиеспобя(В1пб1пду1адя.Оес1агет(бп1у ( В1пб1пду1адз.тпясапсе ) В1пб1пду1адя.рцЬ11с) // Вызвать каждый метод. Еогеасп(меспоб1пго ю гп юх) ( сопяо1е.игаге11пе("Вызов метода (0] ", и.мазе)) // Получить параметры. Рагаюесег1пко[] р1 = ю.деГРагаюесегя()т // Выполнить методы. зи1ссп(р1.1епдГЬ) ( сазе 0: // аргументы отсутствуют 1Е(ю.иесцгптуре ГуреоЕ(гпс)) ( ча1 = (Епс) ю.тпчоне(геЕ1ессОЬ, пц11)т сопяо1е.игаге11пе("Результат: " + ча1)) ) е1зе 1Е(ю.несцгптуре == ГуреоЕ(чо1б)) ( ю.1пчо)те(геЕ1есГОЬ, пц11)т ) Ьгеа)тт саяе 1: // один аргумент гЕ(рг[0).рагаюесегТуре = Гуреог(1пс)) ( оЬбесс() агдз = пен оЬбесс[1]т 544 Часть!. Язык С№ аг9я[0] = 14) гЕ ( (Ьоо1) ж.
1пчоке (геЕ1есГОЬ, аг9я ) ) сопяо1е.иг1ге11пе("значение 14 находится между х и у"); е1яе Сопяо1е.иг1сеЬ1пе("Значение 14 не находится между х и у"); ) Ьгеааз саве 2: // два аргумента 1е((рг[0).Рагаиегегтуре == гуреоЕ(1пг)) ьа (р1[1].Рагажесегтуре = ГуреоЕ()пс))) ( оЬ]есс[) агОя = пен оЬ3есс[2); агдя[0] = 9; аг9я [1] - 18; и.1пчоке(геЕ1есгОЬ, агця)к ) е1яе гЕ((рг[0].рагажесегТуре = гуреоЕ(бопЬ1е)) аа (рг[1].РагажесегТуре == Гуреок(бопЬ1е))) ( оЬ)есс[) агця = пен оЬ]есг[2); агдя[0] 1.12; агдя[1] 23.4; и.1пчоне(геЕ1есГОЬ, агця)к ) Ьгеакы ) Сопяо1е.игггеВ1пе()к ) ) Эта программа дает следующий результат: Использовано: МуС1аяя Конструирование класса ИуС1аяя(гпг] Значение х: 10, значение у: 10 Вызов методов для объекта геЕ1есГОЬ. Вызов метода Япи Результат: 20 Вызов метода 1яВеснееп Значение 14 не находится между х и у Вызов метода Яес В методе Яес(гпс, Епс).
Значение х: 9, значение у: 18 Вызов метода Яес В методе Яес(бопЬ1е, боипте). Значение х: 1, значение у: 23 Вызов метода 5Ьон Значение х: 1, значение у: 23 Глава (7. Динамическая идентификация типов, рефлексия и атрибуты 545 Эта программа работает довольно просто, но все же требует некоторых пояснений.
Вопервых, получаются и используются только те методы, которые явно объявлены в классе мус1аяя. Для этой цели служит форма В1пс(1пор1адя метода Вегмесьос(я (), чтобы воспрепятствовать вызову методов, наследуемых от объекта. И во-вторых, количество параметров и возвращаемый тип каждого метода получаются динамически, а затем определяются и проверяются в операторе яы1гсп. На основании этой информации формируется вызов каждого метода. Атрибуты В языке СК( разрешается вводить в программу информацию декларативного характера в форме аюрибутпа, с помощью которого определяются дополнительные сведения (мета- данные), связанные с классом, структурой, методом и т.д. Например, в программе можно указать атрибут, определяющий тип кнопки, которую должен отображать конкретный класс.
Атрибуты указываются в квадратных скобках перед тем элементом, к которому они применяются. Следовательно, атрибут не является членом класса, но обозначает дополнительную информацию, присоединяемую к элементу. Основы применения атрибутов Атрибут поддерживается классом, наследующим от класса В уя гетп. Аь с г1ьп се. Поэтому классы атрибутов должны быть подклассами класса А( ьг1ьпсе. В классе А( ьг1ьпсе определены основные функциональные возможности, но далеко не все они нужны для работы с атрибутами. В именах классов атрибутов принято употреблять суффикс Аг сг1Ьпсе. Например, Вггогй( Ьг1Ьь се — это имя класса атрибута, описывающего ошибку. При объявлении класса атрибута перед етут именем указывается атрибут Асег1Ьпсецяапе.
Этот встроенный атрибут обозначает типы элементов, к которым может применяться объявляемый атрибут. Так, применение атрибута может ограничиваться одними методами. Создание атрибута В классе атрибута определяются члены, поддерживающие атрибут. Классы атрибутов зачастую оказываются довольно простыми и содержат небольшое количество полей или свойств. Например, атрибут может определять примечание, описывающее элемент, к которому присоединяется атрибут.
Такой атрибут может принимать следующий вид; (Ассг1ьогепяаое (Агсг1ьигетагдегя.А11) ] роь11с с1аяя немагклсгг1ьоге: Агггхьиге ( ягглпд рг1 геваган // базовое поле свойства Певаги рпЬ11с Кееягхлксг1Ьисе(ясг1пи сопвпепс) ( рги гееагк = сопкпепгт роь11с ясгкпд Невагх ( чек ( гегпгп рг1 геваг)тт ) ) 646 часть (.
Язык С№ Проанализируем этот класс атрибута построчно. Объявляемый атрибут получает имя йеваг)кйггг1Ьисе. Его объявлению предшествует встроенный атрибут Агсг1Ьпсе()заче, указывающий на то, что атрибут иевагхйгсг1Ьисе может применяться ко всем типам элементов. С помощью встроенного атрибута Аггг1ьпге() заде можно сузить перечень элементов, к которым может присоединяться объявляемый атрибут. Подробнее о его возможностях речь пойдет далее в этой главе. Далее объявляется класс певагкйьгг№ьисе, наследующий от класса Агсг1ьпге.
В классе ИевагКАГгг№Ьисе определяется единственное закрытое поле рг1 геваг)к, поддерживающее одно открытое и доступное для чтения свойство кеваг)к. Это свойство содержит описание, связываемое с атрибутом. 1конечно, кеваг)к можно было бы объявить как автоматически реализуемое свойство с закрытым аксессором зег, но ради наглядности данного примера выбрано свойство, доступное только для чтения.) В данном классе определен также один открытый конструктор, принимающий строковый аргумент и присваивающий его свойству иеваг)к.
Этим пока что ограничиваются функциональные возможности класса ПевагКАГГг№Ьпге, готового к применению. Присоединение атрибута Как только класс атрибута будет определен, атрибут можно присоединить к элементу. Атрибут указывается перед тем элементом, к которому он присоединяется, и для этого его конструктор заключается в квадратные скобки.
В качестве примера ниже показано, как атрибут иева гкйггг1ьпге связывается с классом. (НевагХАгсг1Ьоге("В этом классе используется атрибут.")! с1аеэ Пэейкгг№Ь ( // В этом фрагменте кода конструйруется атрибут певаг)кАггг№ьиге, содержащий комментарий "в этом классе используется атрибут." Данный атрибут затем связывается с классом ()зейсгг1Ь.
Присоединяя атрибут, совсем не обязательно указывать суффикс йггг№Ьсге. Например, приведенный выше класс может быть объявлен следующим образом: [Певать("В этом классе используется атрибут.")) с1аэя Пяейгсг№Ь ( // В этом объявлении указывается только имя цева гк. Такая сокращенная форма считается вполне допустимой, но все же надежнее указывать полное имя присоединяемого атрибута, чтобы избежать возможной путаницы и неоднозначности. Получение атрибутов объекта Как только атрибут будет присоединен к элементу, он может быль извлечен в других частях программы. Для извлечения атрибута обычно используется один из двух методов.
Первый метод, Сесспзсовйссг1Ьпсеэ (), определяется в классе МевЬег1п1о и наследуется классом туре. Он извлекает список всех атрибутов, присоединенных к элементу Ниже приведена одна из его форм. опзеск[) Пегесэковйкгг1Ьигея(Ьоо1 яеагсллаяея) Если логическое значение эеагс)заалел истинно, то в список включаются атрибуты всех базовых классов, наследуемых по иерархической цепочке. В противном случае атрибуты извлекаются только из тех классов, которые определяются указанным типом. Глава (у. динамическая идентификация типов, рефлексия и атрибуты 547 Второй метод, ПеССпяСощАССг1ЬпСе (), определяется в классе АССг1Ьпке.
Ниже приведена одна из его форм. ясасгс Ассггьпсе песспясопассггьпсе (мещьег1пго щ1, туре ассгкьсуре) где щ1 обозначает объект класса мещЬег1пто, описывающий тот элемент, для которого получаются атрибуты, тогда как а С С ту Ь С уре — требуемый атрибут. Данный метод используется в том случае, если имя получаемого атрибута известно заранее, что зачастую и бываег.