ГЛАВА 12. Отображение типов, динамическое связывание и программирование с помощью атрибутов
Как показано в предыдущей главе, компоновочные блоки являются базовыми элементами установки и среде .NET. С помощью интегрированного обозревателя объектов в Visual Studio 2005 можно рассмотреть открытые типы тех компоновочных блоков, на которые ссылается проект. Внешние средства, такие как ildasm.exe, позволяют увидеть соответствующий CIL-код, метаданные типов и содержимое манифеста компоновочного блока любого бинарного файла .NET, Вдобавок к этим возможностям, доступным во время проектирования компоновочного блока .NET, вы можете получить ту же информацию программными средствами, используя объекты пространства имен System.Reflection. В связи с этим мы выясним роль отображения типов и необходимость использования метаданных .NET.
В оставшейся части главы рассматривается ряд тесно связанных вопросов, относящихся к возможностям сервисов отображений. Например, вы узнаете о том, как .NET-клиент может использовать динамическую загрузку и динамическое связывание для активизации типов., о которых у клиента нет полной информации на этапе компиляции. Вы также узнаете, как с помощью системных и пользовательских атрибутов можно добавить в компоновочный блок .NET пользовательские метаданные. Чтобы продемонстрировать перспективы применения этих (да первый взгляд излишне специальных) возможностей, глава завершится примером построения нескольких "встраиваемых" объектов, которые Вы сможете добавить в расширяемое приложение Windows.Form.
Метаданные типов
Возможность полного описания типов (классов, интерфейсов, структур, перечней и делегатов) с помощью метаданных является главной особенностью платформы .NET. Многие .NET-технологии, такие как сериализация объектов, удаленное взаимодействие .NET и Web-сервисы XML, требуют, чтобы среда выполнения имела возможность выяснить форматы используемых типов. Возможности межъязыкового взаимодействия, поддержка компилятора и возможности IntelliSense среды разработки тоже зависят от конкретного описания типов.
Важность метаданных очевидна и, возможно, именно поэтому они не являются новой идеей, предложенной в рамках .NET Framework. Технологии Java, CORBA и COM уже использовали аналогичные понятия. Например, для описания типов, содержащихся в серверах COM, используются библиотеки COM-типов (по сути, они представляют собой просто скомпилированный IDL-код). Как и COM, библиотеки программного кода .NET также поддерживают метаданные типов. Конечно, метаданные .NET синтаксически совершенно не похожи на IDL (Interface Definition Language – язык описания интерфейсов, используется в COM-технологиях для спецификации интерфейсов объектов COM). Напомним, что просматривать метаданные типов компоновочного блока позволяет утилита ildasm.exe (см. главу 1), Если вы откроете с помощью ildasm.exe любой компоновочный блок *.dll или *.exe, созданный вами в процессе изучения материала этой книги (например, CarLibrary.dll), и нажмете комбинацию клавиш ‹Ctrl+M›, то увидите соответствующие метаданные (рис. 12.1).
Рис. 12.1. Просмотр метаданных компоновочного блока
Как видите, ildasm.exe отображает метаданные .NET-типа очень подробно (двоичный формат оказывается гораздо более компактным). Если бы здесь потребовалось привести описание метаданных компоновочного блока CarLibrary.dll целиком, оно бы заняло несколько страниц. Это было бы лишней тратой вашего времени (и бумаги тоже), так что давайте рассмотрим метаданные только ключевых типов из компоновочного блока CarLibrary.dll.
Анализ метаданных перечня EngineState
Каждый тип, определенный в компоновочном блоке, обозначен маркером "TypeDef #n" (где TypeDef – это сокращение от type definition, что в переводе означает определение типа). Если описываемый тип использует тип, определённый в рамках другого компоновочного блока .NET, то для ссылки на такой тип используется "TypeRef #n" (где TypeRef – это сокращение от type reference, в переводе ссылка на тип). Если хотите, TypeRef можно считать указателем на полное определение метаданных соответствующего типа. По существу, метаданные .NET представляют собой множество таблиц, явно описывающих все определения типов (TypeDef) и все типы, на которые имеются ссылки (TypeRef). Все это можно увидеть в окне просмотра метаданных ildasm.exe.
В случае CarLibrary.dll одно из описаний TypeDef в метаданных соответствуeт перечню CarLibrary.EngineState (у вac номер TypeDef может быть другим: нумерация TypeDef соответствует порядку, в котором компилятор C# обрабатывает соответствующие типы).
TypeDef #1
-------------------------------------------------------------
TypDefName: CarLibrary.EngineState (020000002)
Flags: [Public] [AutoLayout] [Class] [Sealed] [AnsiClass] (00000101)
Extends: 01000001 [TypeRef] System.Enum
…
Field #2
-------------------------------------------------------------
Field Маше: engineAlive (04000002)
Flags: [Public] [Static] [Literal] [HasDefault] (00008056)
DefltValue: (I4) 0
CallCnvntn: [FIELD]
Field type: ValueClass CarLibrary.EngineState
…
Метка TypDefName используется для имени типа. Метка метаданных Extends используется для указания базового класса данного типа .NET (в данном случае это тип System.Enum, обозначенный как TypeRef). Каждое поле перечня обозначено меткой "Field #n". Для примера здесь представлены только метаданные поля EngineState.engineAlive.
Анализ метаданных типа Car
Вот часть дампа типа Car, которая иллюстрирует следующее:
• способ определения полей в терминах метаданных .NET;
• представление методов в метаданных .NET;
• отображение свойства типа в пару специальных членов-функций.
TypeDef #3
-------------------------------------------------------------
TypDefName: CarLibrary.Car (02000004)
Flags: [Public] [AutoLayout] [Class] [Abstract] [AnsiClass] (00100081)
Extends: 01000002 [TypeRef] System.Object
Field #1
-------------------------------------------------------------
Field Name: petName (04000008)
Flags: [Family] (00000004)
CallCnvntn: [FIELD]
Field type: String
…
Method #1
-------------------------------------------------------------
MethodName:.ctor (06000001)
Flags: [Public] [HideBySig] [ReuseSlot] [SpecialName] [RTSpecialName] [.ctor] (00001886)
RVA: 0x00002050
ImplFlags: [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
hasThis
ReturnType: Void
No arguments.
…
Property #1
-------------------------------------------------------------
Prop.Name: PetName (17000001)
Flags: [none] (00000000)
CallCnvntn: [PROPERTY]
hasThis
ReturnType: String
No arguments.
DefltValue:
Setter: (06000004) set_PetName
Getter: (06000003) get_PetName
0 Others
…
Прежде всего, отметьте то, что метаданные класса Car указывают базовый класс типа и включают различные флаги, использовавшиеся конструктором типа при его создании (такие как [public], [abstract] и т.п.). Методы (например, конструктор класса Car) описаны с учетом их имени, параметров и возвращаемого значения. Наконец, обратите внимание на то, что свойства представляются внутренними методами get_ /set_ с использованием меток Setter/Getter метаданных .NET. Как и следует ожидать, производные типы Car (это SportsCar и MiniVan) описываются аналогично.
Анализ TypeRef
Напомним, что метаданные компоновочного блока описывают не только множество внутренних типов (Car, EngineState и т.д.), но и внешние типы, на которые ссылается данный компоновочный блок. Например, поскольку CarLibrary.dll Определяет два перечня, в описании присутствует блок TypeRef для типа System.Enum.
TypeRef #1 (01000001)
-------------------------------------------------------------
Token: 0x01000001
ResolutionScope: 0x23000001
TypeRefName: System.Enum
MemberRef #1
-------------------------------------------------------------
Member: (0a00000f) ToString:
CallCnvntn: [DEFAULT] hasThis
ReturnType: String
No arguments.
Представление метаданных компоновочного блока
Окно метаданных ildasm.exe позволяет также просмотреть метаданные самого компоновочного блока, для обозначения которых используется метка Assembly. Следующий фрагмент листинга показывает, что информация, представленная в таблице Assembly, аналогична информации, получаемой в окне ildasm.exe через пиктограмму MANIFEST (и это совсем не удивительно). Вот часть манифеста CarLibrary.dll (версии 2.0.0.0).
Assembly
-------------------------------------------------------------
Token: 0x20000001
Name: CarLibrary
Public Key: 00 24 00 00 04 80 00 00 // и т.д.