Загрузка сборок и отражение
Сведения о загрузке сборок и отражении полезны для создания динамически расширяемых приложений, то есть таких, для которых хост-приложение создаёт одна компания, а другие - подключаемые компоненты (add-ins), которые расширяют функциональность хоста.
Загрузка сборок
В книге повторяется о механизмах загрузки сборок в домен при обращении к типам этих сборок из кода. Дополнительно рассказывается о возможностях загрузить сборку вручную использую отражение или тип System.AppDomain.
Использование отражения для создания динамически расширяемых приложений
Типы в пространстве имён System.Reflection представляют собой объектную модель для работы с метаданными сборки или модуля. Часто привязку к типам через отражение называют поздним связыванием (late binding) - в отличие от раннего связывания (early binding), когда требуемые типы известны при компиляции.
Производительность отражения
У отражения, несмотря на его достоинства в виде возможности получения информации о типах во время выполнения, есть и два недостатка:
- Отсутствие безопасности типов на этапе компиляции, так как активно используются строки. Например, вызов метода
Type.GetType("int")успешно скомпилируется, но во время выполнения вернётnull, потому что типа с таким именем фактически не существует. - Отражение работает медленно, так как поиск метаданных всегда происходит через сравнение строк без учёта регистра.
Вызов метода или обращение к полю тоже происходит медленно, так как при использовании отражения перед вызовом метода аргументы упаковываются в массив, а затем при вызове извлекаются в стек. Кроме того, CLR приходится проверять правильность числа и типа параметров, а также разрешений доступа у вызывающего кода.
При написании приложения, которое динамически ищет и создаёт объекты, стоит следовать одному из подходов:
- Порождать свои типы от базового типа, известного на момент компиляции, а затем выполнить приведение типа к базовому и вызов виртуальных методов базового типа.
- Реализовывать в типах интерфейсы, известные на момент компиляции, а затем выполнить приведение типа к типу интерфейса и вызвать методы интерфейса.
Нахождение типов, определённых в сборке
С помощью отражения можно находить типы, определённые в сборке.
Объект Type
Тип System.Type - отправная точка для операций с типами и объектами, он представляет ссылку на тип.
Для каждого типа в домене существует единственный объект Type, поэтому определить, принадлежат ли объекты к одному типу можно через операторы равенства и метод получения типа GetType().
Помимо этого, в FCL существует ещё несколько способом получения типа:
- Вызов
System.Type.GetType()с передачей в качестве аргумента полного имени типа с указанием пространства имён. Помимо этого, можно дополнительно указать сборку для поиска типа. - Метод
System.Type.ReflectionOnlyType()работает так же, но загружает тип только для отражения, но не для выполнения кода. - Экземплярные методы
DeclaredNestedTypes()иGetDeclaredNestedTypes()типаSystem.TypeInfo. - Экземплярные методы
GetType(),DefinedTypes()иExportedTypes()типаSystem.Reflection.Assembly.
Вместо этих методов лучше использовать специальный оператор, предназначенный для получения объекта типа. При компиляции такого оператора получается более быстрый код. В C# таким оператором является typeof, хотя обычно его не применяют для сравнения информации о типах, загруженных посредством позднего и раннего связывания. Сравнение с использованием данного оператора проверяет на точное, а не на совместимое (как is и as) сравнение.
Помимо объекта Type информацию можно получить также из TypeInfo путём приведения. Данный тип позволяет получить больше информации о типе, хотя приведение является достаточно затратной операцией, так что не стоит ей злоупотреблять.
Создание иерархии типов, определённых в сборке
Описывается метод для создания иерархии типов на примере производных от типа System.Exception.
Создание экземпляра типа
После получения ссылки на объект, производный от Type, можно создать экземпляр этого типа следующими способами:
- Методы
System.Activator.CreateInstance(). Можно создавать экземпляр, используяTypeили идентифицирующую строку. - Методы
System.Activator.CreateInstanceFrom(). Можно создавать только с использованием строки, определяющей тип и сборку. - Методы объекта
System.AppDomain. - Экземплярный метод
Invoke()объектаSystem.Reflection.ConstructorInfo.
Существует ряд исключений. Экземпляры массивов создаются с использованием статического метода System.Array.CreateInstance(). Для создания делегата используется статический метод System.Delegate.CreateDelegate(). Для создания экземпляра обобщённого типа сначала необходимо получить ссылку на открытый тип, а затем вызвать экземплярный метод System.Type.MakeGenericType() передав аргументы-типы, после чего можно создать экземпляр типа.
Создание приложений с поддержкой подключаемых компонентов
При создании подключаемых компонентов стоит использовать интерфейсы, так как через них можно определить сценарий взаимодействия между модулями. Подробнее в книге.
Нахождение члена типа путём отражения
Для достижения высокой производительности и безопасности типов следует избегать отражения. В динамически расширяемом приложении после создания объекта он обычно приводится к базовому типу или типу интерфейса.
Нахождение членов типа
На рисунках представлена иерархия типов отражения и типы, используемые для обхода объектной модели отражения.
Обращение к членам типов
В книге приводится более подробное описание обращения к членам типа, полученным через отражение, а также примеры кода.
Использование дескрипторов привязки для снижения проблем потребления памяти процессором
Объекты Type и производные от MemberInfo занимают много памяти, если их много и к ним надо часто обращаться. Это способствует снижению производительности.
CLR создаёт эти объекты лишь для упрощения работы программиста, самой среде они не нужны. Для сокращения потребления памяти можно использовать описатели времени выполнения. Они являются значимыми типами с одним единственным полем IntPtr, который представляет собой дескриптор, ссылающийся на тип, поле или метод в куче загрузчика домена приложений. Для приведения данных о типах к описателям существуют специальные статические методы.
