Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Что такое метаданные и зачем они нужны?↑ Стр 1 из 7Следующая ⇒ Содержание книги Поиск на нашем сайте
Метаданные в среде.Net Автор: Андрей Мартынов Опубликовано: 14.03.2003
function ToggleCode(id) { el=document.getElementById(id); img=document.getElementById("img"+id); if(el.style.display=="none") { img.src="/images/ls2.gif"; el.style.display=""; } else { img.src="/images/ls1.gif"; el.style.display="none"; } return false; } Исходные тексты примеров(~100 кб) Что такое метаданные и зачем они нужны? Для того, чтобы различные части программ могли взаимодействовать друг с другом, им необходимо обмениваться информацией о предоставляемых ими возможностях, и о том, каким образом эти возможности использовать. Например, если программа использует статическую библиотеку, к библиотеке обычно прилагается заголовочный файл, описывающий экспортируемые данные, процедуры и структуру типов. Другой пример – DLL. Чтобы использовать её в своей программе, вы, скорее всего, будете использовать соответствующие заголовочный файл и библиотеку импорта. Ещё один пример – COM-компонент. Описание его интерфейса обычно хранится в idl-файле или в виде специальных данных, в виде библиотеки типов. Все эти дополнительные файлы и данные, описывающие программные компоненты, называют метаданными.
В различных технологиях программирования используются метаданные разной степени подробности и разной степени универсальности. Но в технологии.Net метаданные играют совершенно особую роль. Это роль "универсального клея", на который возложены функции поставщика информации о типах как во время компиляции программы, так и во время её исполнения. Эта важная роль метaданных обусловлена следующими факторами: Метаданные в.Net обязательны и универсальны. Каждая программная единица в среде.Net, (сборка), помимо кода на языке MSIL обязательно содержит метаданные, описывающие как её в целом (манифест), так и каждый тип, содержащийся в ней, в отдельности (метаданные). На рисунке 1 показаны составные части однофайловой сборки.
Доступ к метаданным сборки является необходимым и достаточным условием доступа к содержащимся в ней типам. Метаданные в.Net общедоступны. Доступ к метаданным могут получить любые программные компоненты и любые инструменты программирования. Так, компилятор во время компиляции сборки использует информацию о зависимостях между сборками и проверяет соответствие используемых типов, извлекая нужную для этого информацию из метаданных. Инструменты программирования (в том числе среда программирования – IDE), извлекают информацию о типах и в удобном виде представляют структуру классов, а также предоставляют справочную информацию по ним (ObjectBrowser, IntelliSense). Набор утилит, обеспечивающих взаимодействие с COM-компонентами (Regasm, Tlbexp, Tlbimp), целиком опираются в своей работе на метаданные.
Сама сборка во время своего исполнения также имеет доступ к собственным метаданным. Отсюда название соответствующей технологии – Reflection.
Получение информации о типе Каждый тип в среде.Net связан со специальным объектом (объектом-типом). Класс Type играет ключевую роль в системе работы с метаданными. Любая деятельность по извлечению и использованию информации о типе связана с применением этого класса. Экземпляр класса Type позволяет получить полную информацию о типе: информацию о его методах, свойствах, вложенных типах, информацию о сборке и модуле, содержащих данный тип, полное имя типа и многое другое. Более того, с его помощью можно динамически создавать экземпляры описываемого им типа (класса), а также исполнять методы и работать со свойствами полученного объекта. Поэтому работа с типом начинается с получения соответствующего экземпляра класса Type. Динамическая загрузка типов Теперь, когда сборка загружена, можно извлечь из неё информацию о типе. Для этого необходимо использовать так называемое "квалифицированное имя типа" (Assembly Qualified Type Name). Квалифицированное имя типа состоит из двух частей: полного имени типа и полного или частичного имени сборки. Для получения описания метаданных некоторого типа его квалифицированное имя передаётся в статический метод GetType класса Type. В случае успеха этот метод возвращает экземпляр класса Type.
В данном случае загрузка типа проведена в три этапа. Сначала загружена сборка, затем получено её полное имя, и только потом получен объект Type. Эти этапы можно объединить. Если вы знаете полное имя сборки, можно использовать его для составления квалифицированного имени типа, которое можно напрямую передать методу Type.GetType().
Исследование типа Имея в руках объект Type, можно начинать исследовать структуру типа, который он описывает (перебирать поля, методы, события, свойства, вложенные типы...). Члены класса Для получения информации о членах класса имеется несколько групп методов (далее – семейств). Каждая группа обеспечивает получение информации об определённом типе членов класса. Например, информацию только о конструкторах класса, или только о его полях. Но есть одна группа методов, которые позволяют получать информацию сразу обо всех членах класса. Это семейство методов GetMembers.
В каждом семействе имеется не меньше двух методов, аналогичных приведенным выше. Один из этих методов возвращает массив, содержащий информацию об открытых (public) членах, а другой позволяет указать, информацию о каких членах класса следует возвращать. Например, если передать в качестве параметра BindingFlags.Public | BindingFlags.Instance, будут возвращены все открытые нестатические члены класса. Если вам нужна информация ещё и о статических членах, нужно добавить ещё один флаг BindingFlags.Static. А если вас интересуют не только открытые, но и закрытые члены, следует использовать флаг BindingFlags.NonPublic. Ниже перечислены основные семейства методов класса Type, используемые для получения информации о членах класса.
Поскольку и конструкторы, и поля, и события, и методы, и свойства - это всё члены класса, то совершенно логично, что соответствующие им типы метаданных, (ConstructorInfo, EventInfo, FieldInfo, MethodInfo, PropertyInfo) унаследованы от типа MemberInfo. Давайте рассмотрим пример получения информации обо всех конструкторах класса.
В этом примере вы можете видеть, каким способом извлекается информация о параметрах методов. Каждая структура данных, отвечающая за методы-члены (EventInfo, MethodInfo, ConstructorInfo), имеет метод GetParameters(), возвращающий массив элементов типа ParameterInfo. Каждый элемент этого массива описывает отдельный параметр метода: его тип, имя, значение по умолчанию, направление передачи данных (in/out) и др. Надеюсь, я убедил вас, что в получении информации о типах нет ничего сложного. Информации этой много, и она очень подробна, но извлекать её довольно просто (особенно когда точно знаешь, что именно тебе нужно). Исследование объекта Итак, предположим, мы изучили класс досконально. Теперь мы знаем о нём всё. Как же воспользоваться этим знанием, если имеется готовый экземпляр класса? Как прочитать его поля, как получить значения его свойств, как вызывать его методы? Давайте для начала научимся читать поля и свойства объекта.
Предположим, у вас имеется ссылка на объект неизвестного типа. В.Net это означает, что имеется ссылка на объект типа Object. Тип Object – это базовый тип CLR, и к нему можно привести любую ссылку (value-типы перед этим должны быть помещены в GC-хип, т.е. должна быть произведена операция boxing'а). У Object есть только базовый набор методов, через которые нельзя напрямую вызвать методы типа, приведенного к Object. Но с помощью Reflection можно динамически получить описание методов, полей или свойств объекта. А с помощью этого описания можно динамически к ним обращаться. Следующий пример считывает список полей и выводит их имена и значения:
А этот выводит список свойств объекта:
Трассировка полей объекта В процессе отладки иногда возникает необходимость запротоколировать состояние некоторых важных для работы программы классов. Написание кода протоколирования полей – не самая интересная работа, да и времени она может отнять много. К тому же может оказаться, что самое интересное поле класса всё-таки осталось не выведенным, а ситуация была трудновоспроизводимой... В таком случае можно порекомендовать вывести просто все (!) поля класса, тем более, что механизм reflection позволяет делать это одной строчкой кода.
Трассировка полей объекта
Вы легко сможете модифицировать код этой процедуры, если потребуется изменить формат вывода или выводить дополнительно свойства класса или другие его компоненты. Обращу ваше внимание на то, что здесь используется вариант метода GetFields, позволяющий получать доступ как к открытым полям класса, так и к закрытым. Надо отметить, что доступ к закрытым членам возможен не всегда. Для чтения информации о типах необходимо, чтобы запрашивающий код имел по всей глубине стека вызовов привилегию ReflectionPermission с флагом TypeInformation.
Привилегии, разграничение доступа, система безопасности.Net – эти темы требуют отдельного, обстоятельного разговора, а пока воспользуемся тем, что в большинстве случаев метаданные доступны для чтения, и испытаем в деле процедуру протоколирования полей объекта. Вот пример использования предложенного способа вывода полей класса.
Вот что должно вывестись на экран:
Хочу обратить ваше внимание на имеющиеся здесь подводные камни. Посмотрим на поле типа string. Тип string имеет статическое поле Empty того же типа string. Поэтому, выводя поле Empty, вы снова обнаружите у него статическое поле Empty, выводя которое вы снова обнаружите статическое поле Empty... короче, получается бесконечная рекурсия. Выход простой – не выводить второй раз статические поля, а ссылаться на первый вывод этих полей. Второй камушек связан с взаимными ссылками объектов друг на друга. В последнем примере класс Class1 имеет ссылку на класс Data1, а тот, в свою очередь, имеет ссылку на Class1. Если не предпринять специальных мер, то опять получится бесконечная рекурсия. Меры примерно такие же, как в предыдущем случае – запоминаем все выведенные объекты и не выводим их второй раз.
Динамический вызов методов На самом деле мы только что уже занимались динамическим вызовом методов. С конструкторами, по крайней мере, разобрались. Вызов остальных методов производится без помощи дополнительных классов типа Activator. Этим занимается сам класс Type. Динамический вызов метода динамически созданного объекта:
Если вызываемый метод перегружен, будет вызван тот вариант метода, который имеет те же типы параметров, причем в том же порядке, что и типы элементов переданного массива параметров. Использование интерфейсов Для вызова методов динамически созданного объекта можно применять гораздо более удобный и эффективный способ. Правда, этот способ работает, только если вы знаете, что объект реализует известный вам на момент компиляции интерфейс. Использование динамически созданного объекта через интерфейс
Очевидно, что эффективность вызова метода динамически созданного объекта через интерфейс гораздо выше, чем через InvokeMember, ведь это обычный вызов виртуального метода. Поэтому, если есть такая возможность, лучше использовать интерфейсы. Для получения ссылки на интерфейс ICloneable здесь применяется операция as языка C#. Эта операция возвращает ссылку на интерфейс, если он реализован объектом. Если объект не поддерживает данный интерфейс, будет возвращена пустая ссылка (null). Ниже будут подробнее рассмотрены другие способы динамического приведения типов. Позднее связывание Ну вот, теперь можно сказать, что мы умеем работать с типами, определяемыми в момент исполнения, почти столь же свободно, как и с типами, известными нам на момент компиляции. (Если я и преувеличил, то совсем чуть-чуть. 8-). Теперь мы вправе заслуженно насладиться плодами вновь освоенных технологий. Предлагаю вам в качестве небольшой передышки рассмотреть позднее связывание. Традиционная постановка задачи позднего связывания: предположим, у нас есть программа, позволяющая выполнять обработку каких-либо файлов. Для обработки используются классы, реализующие интерфейс FileProcessor. Список типов обрабатываемых программой файлов и соответствующих им классов-обработчиков постоянно пополняется. Сборки, содержащие новые классы-обработчики, помещаются в отдельную папку, а программа-обработчик файлов должна уметь использовать всё новые и новые классы-обработчики без перекомпиляции. Не сомневаюсь, что многие из читающих эти строчки решали подобные задачи. Причем в разные времена эти задачи решались по-разному, начиная от LoadLibrary и GetProcAddress, и кончая COM-интерфейсами и категориями. Посмотрим, какое решение этой классической задачи предлагает технология.Net. Интерфейс класса-обработчика будет иметь единственную функцию, определяющую, может ли данный класс обработать данный файл. Поместим интерфейс в отдельную сборку. На неё будут ссылаться конкретные классы-обработчики. Интерфейс-обработчик.
Теперь определим пару классов обработчиков. Один из них будет работать с текстовыми файлами, второй – с картинками в формате ВMP. При этом каждый будет реализовывать интерфейс-обработчик, и будет помещён в отдельную сборку, ссылающуюся на сборку с интерфейсом (см. рис.3). Обработчик текстовых файлов.
Обработчик картинок.
В самом приложении, осуществляющем позднее связывание с классами-обработчиками, для вас уже почти ничего нового нет. Здесь просто загружаются все сборки из каталога приложения. Затем запрашиваются все типы из каждой сборки, и среди типов ищутся те, что унаследованы от абстрактного класса-обработчика. После того, как нужный тип найден, создаётся его экземпляр и вызывается метод IsSupport. Приложение, использующее позднее связывание с классами-обработчиками.
Теперь, если появится необходимость обрабатывать файлы, например, формата VRML, не потребуется дорабатывать и перекомпилировать приложение. Достаточно будет разработать соответствующий класс-обработчик VRMLProcessor и поместить его в ту же папку, что и остальные классы-обработчики. Атрибуты Говоря о метаданных и рефлексии, нельзя не коснуться такой темы, как атрибуты. Ведь когда говорится о таком важном свойстве метаданных, как их расширяемость, то подразумеваются именно атрибуты. Атрибуты позволяют дополнить информацию о любом элементе программы. Атрибуты могут использоваться любыми программами, в том числе компиляторами, визуальными средствами разработки и конечными приложениями.
Новые механизмы абстракции? Теперь, когда мы завершили краткое рассмотрение методов исследования типов (методов рефлексии), у нас есть повод задуматься над вопросом: "Какое новое качество могут принести методы рефлексии в наши программы. Дают ли они в наши руки принципиально новые возможности? Думаю этот вопрос имеет положительный ответ. Напомню, что в С++ (а может и во всей идеологии ООП) существуют два принципиально разных подхода к разработке абстрактных алгоритмов и абстрактных структур данных. Первый подход основан на наследовании и виртуальных методах, в основе второго лежат шаблоны. Формулируя алгоритм с помощью абстрактных типов данных (шаблонов), вы предполагаете наличие определённых свойств у типов, которые будут «подставлены» в шаблон (параметров шаблона). Конкретная реализация алгоритма становится ясной тогда, когда становятся известны типы-параметры шаблона, т.е. во время компиляции программы, использующей шаблон. («конкретизация при компиляции») Формулируя алгоритм с помощью абстрактных (виртуальных) методов класса, вы откладываете окончательную реализацию алгоритма на время исполнения программы. Именно тогда, во время исполнения, решается то, какие реальные методы будут участвовать в работе алгоритма. Это зависит от того с какими конкретными объектами придётся работать алгоритму. Можно сказать, что виртуальные методы конкретизируются в runtime. («конкретизация времени исполнения»). Но заметьте, виртуальными (абстрактными) до сих пор могли быть только методы. А если вам нужно применить тот же самый механизм конкретизации времени исполнения, но по отношению к полям объекта? А если вам необходимы абстрактные структуры данных, конкретизируемые не при компиляции, а во время исполнения? Всё это позволяет сделать reflection. Короче, reflection – это виртуализация всех элементов программы. Теперь нам позволительно формулировать алгоритмы подобные таким: «Найди в этой структуре поле обозначающее цену товара и верни её». Перебираем все поля класса, в поисках поля типа Currency. Заметьте вам не надо знать имя поля. Если вы знаете что поле такого типа есть и оно одно – этого достаточно! «Свяжи поля этой структуры с аргументами той хранимой процедуры на основе типов и имён полей и параметров». В последнем номере русского MSDN отличная статья про подобного рода методы. «Выведи на консоль поля c атрибутом «видимый» (этим мы только что занимались). «Предложи пользователю выбрать класс, заполнить параметры его конструктора класса и создай экземпляр этого класса». Эту задачу и многие подобные ей решает программа.Net Explorer описанная в статье этом номере нашего журнала. У меня складывается впечатление, что наконец то мы с вами стали настоящими хозяевами своих программ! Наконец то нам стало доступно то, что раньше мог делать только «его величество компилятор». Уверен, что эти новые возможности породят совершенно новые классы алгоритмов, новые подходы к программированию, что в свою очередь принесёт новое качество в наши программы. Поживём – увидим. А пока рассмотрим ещё один приём программирования в среде.Net, тесно связанный с метаданными, и сулящий не менее революционные изменения в методах программирования. Динамическое создание типов Да. Заголовок этой главки написан правильно. Опечатки нет. Именно создание типа в момент исполнения программы. То есть в каком-то смысле программы начинают программировать себя сами... Динамический "Hello World!" Значит, начинаем процесс обучения программ программированию? Тогда не будем отступать от традиций и начнём обучение с классической программы "Hello World!". Динамический вариант "Hello World!"
Класс-программист (класса Programmer) пишет код методом WriteCode(). Код – это метод класса HelloWorldClass, который содержится в модуле HelloWorldModule, который принадлежит сборке HelloWorldAssembly. Класс-программист создаёт эти объекты с помощью набора соответствующих объектов-Buider'oв, прописанных в пространстве имён System.Reflection.Emit, попутно задавая атрибуты создаваемых объектов. В данном случае и тип, и метод создаются как открытые (об этом говорят флаги TypeAttributes.Public и MethodAttributes.Public). Самое интересное, конечно – это непосредственное генерирование кода. Он в данном случае состоит всего из двух команд языка IL: вывод строки на консоль и возврат из процедуры. Забавно? Только и всего?
Заключение Мне же на этом пора заканчивать. Надеюсь, мне удалось вызвать у вас интерес к обсуждаемой теме и помочь сделать первые шаги в столь увлекательной и перспективной области, как программирование с использованием метаданных. Удачи! Метаданные в среде.Net Автор: Андрей Мартынов Опубликовано: 14.03.2003
function ToggleCode(id) { el=document.getElementById(id); img=document.getElementById("img"+id); if(el.style.display=="none") { img.src="/images/ls2.gif"; el.style.display=""; } else { img.src="/images/ls1.gif"; el.style.display="none"; } return false; } Исходные тексты примеров(~100 кб) Что такое метаданные и зачем они нужны? Для того, чтобы различные части программ могли взаимодействовать друг с другом, им необходимо обмениваться информацией о предоставляемых ими возможностях, и о том, каким образом эти возможности использовать. Например, если программа использует статическую библиотеку, к библиотеке обычно прилагается заголовочный файл, описывающий экспортируемые данные, процедуры и структуру типов. Другой пример – DLL. Чтобы использовать её в своей программе, вы, скорее всего, будете использовать соответствующие заголовочный файл и библиотеку импорта. Ещё один пример – COM-компонент. Описание его интерфейса обычно хранится в idl-файле или в виде специальных данных, в виде библиотеки типов. Все эти дополнительные файлы и данные, описывающие программные компоненты, называют метаданными.
В различных технологиях программирования используются метаданные разной степени подробности и разной степени универсальности. Но в технологии.Net метаданные играют совершенно особую роль. Это роль "универсального клея", на который возложены функции поставщика информации о типах как во время компиляции программы, так и во время её исполнения. Эта важная роль метaданных обусловлена следующими факторами:
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Последнее изменение этой страницы: 2016-07-14; просмотров: 738; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.116.40.188 (0.011 с.) |