|
|
Применение рефлексии для создания плагинов
|
|
Страница 1 из 3 Плагины стали неотъемлемой частью больших коммерческих приложений. С их помощью можно наращивать функциональность приложений без повторной компиляции или быстро изменять бизнес-правила, на основе которых работает приложение. Кроме того, для разработки плагинов не нужно иметь доступа к исходному коду приложения, поэтому они могут разрабатываться сторонними организациями. В .NET написание плагинов является простой задачей, которая решается с помощью рефлексии (reflection). Рефлексия позволяет динамически загружать сборки, получать информацию о методах, свойствах, событиях и полях классов из сборок, создавать новые типы и вызывать методы во время выполнения. Классы и интерфейсы для рефлексии находятся в пространстве имен System.Reflection.
В этой статье мы рассмотрим создание плагинов и их подключение к приложению с помощью т.н. позднего связывания. Со сборками, в которых будут находится плагины, мы будем работать с помощью класса Assembly. Сборка может быть загружена с помощью статических методов класса Assembly Load, LoadFrom и LoadWithPartialName. Load загружает сборку по ее имени, заданным строкой, или на основе информации хранящейся в объекте AssemblyName (версия, криптографический ключ, ифнормация о культуре). В имя сборки не входит расширения файла, в котором она находится. Например, имя сборки (MyAsm.dll будет MyAsm). LoadFrom напрямую загружает сборку из файла, путь к которому передается методу. Метод LoadWithPartialName загружает сборку при неполных сведениях о ней, но пользоваться им не рекомендуется из-за непредсказуемости его работы, т.к. он был разработан для бетта-тестеров .NET Framework. Можно загружать сборки и вызовом метода Load для объектов домена AppDomain. Например, чтобы загрузить сборку в текущий домен можно воспользоваться таким кодом AppDomain.CurrentDomain.Load(assemblyName); Основной класс для динамического получения информации о классах, интерфейсах, их полях, методах и перечислениях - Type. Для получения объекта Type можно воспользоваться несколькими разными методами: - статический метод Type.GetType, который по имени типа возвращает объект Type
- методы GetInterface, GetInterfaces, FindInterfaces, GetElementType и GetTypeArray класса Type
- методы GetType, GetTypes и GetExportedTypes класса Assembly
- методы GetType, GetTypes и FindTypes класса Module
- оператор typeof
Когда мы получили объект Type для какого-то типа, то у нас есть множество способов получить информацию о нем. Например, с помощью метода GetFields можно получить массив объектов FieldInfo с информацией о методах, а свойством IsSealed можно определить, объявлен ли тип как sealed. Создание экземпляров типов По объекту Type можно не только определять параметры типа, но и создавать его экземпляры и вызывать их методы. Для этого также существует несколько методов: - методы CreateInstance, CreateInstanceAndUnrap, CrateInstanceFrom и CrateInstanceFromAndUnrap класса AppDomain. После вызова методов, названия которых не оканчиваются на AndUnrap, для доступа к реальным данным нужно вызывать дополнительную функцию Unrap, т.к. эти методы возвращают wrapper (объект класса ObjectHandle) для нового экземляра типа
- методы CreateInstance и CreateInstanceFrom класса Activator. Это специальный класс для создания экземпляров типов и получения ссылок на удаленные объекты. Методу CreateInstance передаются объект Type или название инстанцируемого типа, массив объектов, соответствующих параметрам конструктора типа и объекты CultureInfo. Методу CreateInstanceFrom дополнительно передается имя сборки, содержащий тип. Методы, не принимающие в качестве параметра объект Type, также возвращают wrapper's ObjectHandle
- метод CreateInstance класса Assembly, создающий тип по его имени
- метод Invoke класса ContructorInfo
- метод InvokeMember класса Type
Использование интерфейсов При создании плагинов обычно используются интерфейсы, определяющие методы и свойства, которые должны реализовываться плагином. Для получения интерфейсов, которые есть у типа, используются методы GetInterface,GetInterfaces и FindInterfaces класса Type. Метод GetInterface по имени интерфейса позвращает объект Type для этого интерфейса или null если такого интерфейса у типа нет. Метод GetInterfaces возвращает массив объектов Type с информацией об интерфейсах. Метод FindInterfaces возвращает массив интерфейсов, выбранных с помощью фильтра - делегата, вызываемого для каждого интерфейса. Если класс реализует несколько интерфейсов, у которых есть методы с одинаковыми названиями, то нужно использовать метод GetInterfaceMap класса Type. Он возвращает объект InterfaceMapping для определения соотношения методов интерфейсов и методов класса, которые их реализуют. Вызов методов Обычно методы вызываются с помощью метода InvokeMember класса Type. Процесс вызова метода состоит из двух этапов - привязки, при котором находится нужный метод, и непосредственно вызова. Для вызова нужно указать - имя метода (в качестве метода может быть обычный метод, конструктор, свойство или поле)
- битовую маску из значений BindingFlags для поиска метода. В маске можно указать тип доступа метода, тип метода (поле, свойство, ...), тип данных и пр.
- объект Binder для связывания членов и аргументо
- объект, у которого вызывается метод
- массив аргументов метода
- массив объектов ParameterModifier
- объект CultureInfo
Разработка плагинов Для демонстрации применения рефлексии при создании плагинов было разработано небольшое тестовое приложение, состоящее из 4 проектов. - MainApp - основное приложение, к которому будут подключаться плагины. Приложение загружает из графических файлов изображения и выводит их на форме
- Interface - определяет интерфейсы IPlugin для плагинов и IMainApp для приложений, к которым будут подключаться плагины
- RandomPlugin и ReversePlugin - плагины для добавления шума к изображениям и отражения изображения по вертикали
Проект Interface содержит только определения двух интерфейсов. Приложение, которое подключает плагины, должно реализовывать интерфейс IMainApp. Этот интерфейс объявляет единственное свойство Image, с помощью которого плагины получают изображение и возвращают его после преобразования. public interface IMainApp { Bitmap Image { get; set; } } Интерфейс для плагинов называется IPlugin и содержит объявления трех свойств и одного метода Transform для преобразования изображения. Свойства используются для получения информации о плагинах - названия, номера версии и автора. Методу передается интерфейс IMainApp. Если бы наши плагины содержали бы несколько методов для преобразования изображения, то можно было поступить другим образом - создать в плагине метод для передачи в плагин интерфейса IMainApp, чтобы не передавать его каждому методу. Плагин тогда содержал бы в себе ссылку на главное приложение. public interface IPlugin { string Name { get; } string Version { get; } string Author { get; } void Transform(IMainApp app); } Если бы наше приложение использовало какие то типы (классы, интерфейсы, перечисления, ...), которые бы использовались или передавались плагинам, то их тоже нужно было бы поместить в сборку Interface.
|
|
-
.NET Reflection,
Динамический поиск подключаемых модулей
Расширяет инфраструктуру для добавления поддержки подключаемых модулей в ваши .NET-приложения, чтобы вы также могли осуществлять динамический поиск подключаемых модулей в собственном каталоге приложения. Прежде всего, эта статья — дополнение к моей предыдущей статье о подключаемых модулях. Я рекомендую вам, прежде чем погрузиться в эту статью, ознакомиться с первой. Основная цель этой статьи — избавить пользователя от файлов конфигурации. Основная мысль — обеспечить, что...
-
.NET Reflection,
Создание подключаемой инфраструктуры
Люди, как правило, добавляют поддержку подключаемых модулей в свои приложения по следующим причинам: - Чтобы расширить функциональные возможности приложения без необходимости перекомпиляции или повторного распространения его среди заказчиков. - Чтобы добавить функциональные возможности без необходимости доступа к оригинальному исходному коду. - Бизнес-правила для приложения меняются часто или часто добавляются новые правила....
-
.NET Reflection,
Прикладное применение рефлексии в .NET
NET Reflection представляет собой классический пример некоторой низкоуровневой библиотеки, которая может быть использована при решении прикладных задач. Что же это такое?Рефлексия (ударение на последнем "и", синоним слова интроспекция), или, по-английски, reflection - система, предоставляющая выполняемому коду информацию о нем самом. Звучит немного запутанно, и, как всегда, намного проще понять суть на примере....
|
|
|