Г. Шилдт - С#4.0 Полное руководство (1160795), страница 106
Текст из файла (страница 106)
Наще. Сощрагето ("БЬон" ) ==0) ( .щ.1пчохе(ге11есГОЬ, по11); ) ) ) ) При выполнении этой программы получается следующий результат. Найдено: МуС1авв Найдено: АпоснегС1авв Найдено: Оещо Использовано: МуС1авв Доступные конструкторы: МуС1авв(1пг32 1) Мус1авв(1пг32 1, гпг32 3) Найден конструктор с двумя параметраыи. Конструирование класса МуС1авв(гпс, Тпг) Значение х: 10, значение у: 20 Вызов методов для объекта гет1есГОЬ Сумма равна 30 Значение 14 находится между х и у В методе Бес(1пс, гпс). Значение х: 3, значение у: 18 В методе Вес(боиЬ1е, СопЬ1е). Значение х: 1, значение у: 23 Значение х: 1, значение у: 23 Как следует из результата выполнения приведенной выше программы, обнаружены все три класса, содержащиеся в файле сборки МуС1ав зев .
ехе. Первым среди них обнаружен класс МуС1а за, который затем был использован для получения экземпляра объекта и вызова соответствующих методов. Отдельные типы обнаруживаются в сборке МуС1ав вез . ехе с помощью приведенной ниже последовательности кода, находящегося в самом начале метода,мадп () . Загрузить сборку МуС1аввев.ехе. АввещЬ1у авщ = АввещЬ1у.ьоадггощ("Мус1аввев.ехе") // Обнаружить типы, содержащиеся в сборке МуС1аввев.ехе.
Туре(] а11гурев = авщ.оестурев(); Гогеасн(туре Гещр ьп а11сурев) сопво1е.хгтгеьтпе("найдено: " + гещр.маме); Этой последовательностью кода можно пользоваться всякий раз, когда требуется динамически загружать и опрагпивать сборку. Но сборка совсем не обязательно должна быть исполняемым файлом с расширением . ехе. Сборки могут быль также в файлах динамически компонуемых библиотек 560 Часть ).
Язык С№ (РЕЕ) с расширением . с)11. Так, если скомпилировать исходный файл МуС1аязея. ся в следующей командной строке; сяс )ги11ькаку ИРС1аввея.св то в итоге получится файл МуС1ая яея . с)11. Преимущество размещенил кода в библиотеке РЕЕ заключается, в частности, в том, что в этом случае метод Ма№п () в исходном коде не нужен, тогда как всем исполнлемым файлам требуется определенная точка входа, с которой должно начинаться выполнение программы. Именно поэтому класс Г)ево содержит метод Маьп () в качестве такой точки входа. А для библиотеки РЕЕ метод Ма ьп ( ) не требуется. Если же класс МуС1 аз я нужно превратить в библиотеку ГШ., то в вызов метода Ьоас)ртов () прьтдется внести следующее изменение.
АяяевЬ1у аяв = АяяевЬ1у.ъоаг)Ггов("Мус1аяяея.б11") Полностью автоматизированное обнаружение типов Прежде чем завершить рассмотрение рефлексии, обратимся к еще одному поучительному примеру. Несмотря на то что в программе из предыдущего примера класс Мус1азз был полноценно использован без явного указания на его имя в программе, этот пример все же опирается на предварительную осведомленность о содержимом класса МуС1азз. Так, в программе были заранее известны имена методов Эег и Бпв из этого класса. Но с помощью рефлексии можно воспользоваться типом данных, ничего не зная о нем заранее.
С этой целью придется извлечь все сведения, необходимые для конструирования объекта и формирования вызовов соответствующих методов. Такой подход может оказаться пригодным, например, при создании инструментального средства визуального проектирования, поскольку он позволяет использовать типы данных, имеющиеся в системе. Рассмотрим следующий пример, демонстрирующий полносп ю автоматизированное обнаружение типов.
В этом примере сначала загружается сборка МуС1аяяея. ехе, затем конструируется объект класса МуС1а за и далее вызываются все методы, объявленные в классе МуС1азя, причем о них ничего заранее неизвестно. // Использовать класс МуС1аяя, ничего не зная о неы заранее.
пяьпэ Зуягев; пя1пэ Зуягев.кет1есгьоп; с1аяя йет1есгкяяевЬ1уоево ( ягаггс чоьг) Магп() ( 1пг ча1; АяяевЬ1у аяв = АяяевЬ1у.ъоадтгов("Иус1аяяе*.ехе")г туре() а11гурев = аяв.оегтурея() Туре С = а11сурея(0)г // использовать первый обнаруженный класс Сопяо1е.нгьгеъьпе("Использовано: " + С.каве)Г Сопяггпсгог1пто() сь = с.сегсопясгссгогя()Г Использовать первый обнаруженный конструктор. Рагавегеггпто() ср1 = с1[0].6еграгавегегя()г оЬ)есг ге11есСОЬГ Глава 17. Динамическая идентификация типов, рефлексия и атрибуты 561 11(ср1.ЬепОСЬ > 0) ( оЬ]есг(] сопяаг9я = пен оЬОесг(ср1.ЬепОСЬ] // Инициализировать аргументы.
Тот(1пг п=О; и < срт.сепОСЬ; и++) сопяагОя(п] = 10 + и * 20; Сконструировать объект. ге11ессОЬ = с1(0].1пчосе(сопяаг9я)т ) е1яе ге11есСОЬ = с1(0].1пчосе(пц11); Сопяо1е.нг1СеЬ1пе("1пВызов методов для объекта геЕ1есСОЬ.") Сопяо1е.Хгтоектпе(): // Игнорировать наследуеьые методы. месьос1пто(] в1 = с.Оесмесьодя(В1пбтпОР1а9я.бес1агеббп1у В1поспЗР1а9я.1пягапсе Втпб1пдР1аОя,рцЬ11с)т // Вызвать каждый метод.
Тогеасл(иегсоб1пто в Ьп вт) ( Сопяо1е.игтгеЬ1пе("Вызов метода (0) ", в.Мазе) Получить параметры. РагавеСег1пто() рь = в.ОеСРагавегегя() // Выполнить методы. янтгсЬ(рТ.ЬепОСЬ) сазе 0: // аргументы отсутствуют 11(в.кегцгптуре == Суреот(тпг)) ( ча1 = (Тпт) в.1пчосе(ге11есСОЬ, пц11); Сопяо1е.иг1СеЬ1пе("Результат: " + ча1); ) е1яе 11(в.кегягпТуре == Суреос(чове)] ( в.1пчосе(ге11ессОЬ, пц11)т Ьгеах; саяе 1: // один аргумент 11(рт(0].рагавеоегтуре == Суреот(тпо)) ( оЬ]есг[] агдя = пек оЬ]есг[1); агОя(0] = 14; 11((Ьоо1) в.1пчосе(ге11есСОЬ, агся)) Сопяо1е.игтсеЬгпе("Значение 14 находится между х и у")) е1яе Сопяо1е.нгтгеЬЬпе("Значение 14 не находится между х и у"); ) Ьгеая; саяе 2: // два аргумента те((р1(0].рагавесегтуре == суреот(тпс)) 44 (рт(1).РагавегегТуре == Суреот(1пг))) ( оь]есс[] агся = пен оь]есс[2]; агОя(0] = Ры 562 Часть!. Язык С№ агдя [1) = 10) з.1пноке(тег1есьоь, агдя)4 е1яе №Г((рг[0).Рагазесегтуре == Ьурео№(оообге)) яя (рг[1).Рагаиегегтуре == Ьурео№(бооЬ1е))) оЬЗесс[] агдя = пен оЬЗесс[2); агдя[0) = 1.12; агдя[1) = 23.4," е.1пчоие(ге11есЬОЬ, атдя); ) Ьгеатп ) Сопяо1е.нгьсеьтпе(); Эта программа дает следующий результат.
использовано: мус1аяя Конструирование класса МуС1аяя(ьпг) Значение х: 10, значение у: 10 Вызов методов для объекта ге№1есгОЬ. Вызов метода Бои Результат: 20 Вызов метода 1явегмееп Значение 14 не находится между х и у Вызов метода Бег В методе Бес(ьпс, гпг). Значение х: 9, значение у: 1В Вызов метода Бег В методе Бег(бооЬ1е, бооЬ1е). Значение х: 1, значение у: 23 Вызов метода БЬон Значение х: 1, значение у. "23 Эта программа работает довольно просто, но все же требует некоторых пояснений. Во-первых, получаются и использую~од только те методы, которые явно объявлены в классе МуС1азз. Для этой цели служит форма Вгпг)1пдр1адз метода СеЬМеЬЬос[з (), чтобы воспрепятствовать вызову методов, наследуемых от объекта.
И во-вторых, количество параметров и возвращаемый тип каждого метода получаются динамически, а затем определяются и проверяются в операторе зы№ссЬ. На основании этой информации формируется вызов каждого метода. Атрибуты В С№ разрешается вводить в программу информацию декларативного характера в форме а)лрибул4а, с помощью которого определяются дополнительные сведения (метаданные), связанные с классом, структурой, методом и т.д. Например, в програм- Глава 17. Динамическая идентиФикация типов, рефлексия и атрибуты 563 ме можно указать атрибут, определяющий тип кнопки, которую должен отображать конкретный класс.
Атрибуты указываются в квадратных скобках перед тем элементом, к которому они применяются. Следовательно, атрибут не является членом класса, но обозначает дополнительную информацию, присоединяемую к элементу. Основы применения атрибутов Атрибут поддерживается классом, наследующим от класса Буясет. АссгьЬисе.
Поэтому классы атрибутов должны быть подклассами класса Аггг1Ьпге. В классе АГГ гьЬпсе определены основные функциональные возможности, но далеко не все они нужны для работы с атрибутами. В именах классов атрибутов принято употреблять суффикс Аггг1Ьпге. Например, Еггогйггг1Ьцге — это имя класса атрибута, описывающего ошибку. При объявлении класса атрибута перед его именем указывается атрибут Аггг1Ьпге(]заде. Этот встроенный атрибут обозначает типы элементов, к которым может применяться объявляемый атрибут. Так, применение атрибута может ограничиваться одними методами.
Создвннеатрибутв В классе атрибута определяются члены, поддерживающие атрибут. Классы атрибутов зачастую оказываются довольно простыми и содержат небольшое количество полей или свойств. Например, атрибут может определять примечание, описывающее элемент, к которому присоединяется атрибут. Такой атрибут может принимать следующий вид.
(АГГг1Ьисеояаэе (АГГг1Ьпсетагсеся. А11] ] рпЬ11с с1аяя КепагХАГГг1Ьаее: АГГгьЬите ( ясггпэ ргг геаагк; // базовое поле свойства Кеиагк рпЬ1гс НееагХАГГг1Ьпсе(ясггпэ соаеепс] ( ргг геаагк = сопввепг; ] риЬ11с ясггпс Кеяагк ( дег гегпгп ргь гетаг]н ] ] Проанализируем этот класс атрибута построчно. Объявляемый атрибут получает имя НеыагХАГГг1Ьпсе.
Его объявлению предшествует встроенный атрибут АГГг1Ьпсе(]заде, указываЮщий на то, что атрибут неиагхдггг1ьпге может применяться ко всем типам элементов. С помощью встроенного атрибута Ассг1Ьпсеняапе можно сузить перечень элементов, к которым может присоединяться объявляемый атрибут. Подробнее о его возможностях речь пойдет далее в этой главе. Далее объявляется класс НевагХАГГг1Ьпте, наследующий от класса Ассг1Ьпсе. В классе Неыагкйссг1Ьпсе определяется единственное закрытое поле ргг геыагк, поддерживающее одно открытое и доступное для чтения свойство неыагх.