Вы видели хоть один проект на C# не использующий DLL? А серьезный не модульный проект? В любом случае рано или поздно для Вас данный вопрос будет критичным. Для остальных данная статья не представляет интереса, т.к. здесь описаны лишь базовые понятия с примером реализации рефлексии.
В .NET написание плагинов является простой задачей, которая решается с помощью рефлексии (reflection). Рефлексия позволяет динамически загружать сборки, получать информацию о методах, свойствах, событиях и полях классов из сборок, создавать новые типы и вызывать методы во время выполнения. Классы и интерфейсы для рефлексии находятся в пространстве имен System.Reflection.
Для начала напишем два dll-модуля, в классах которых реализуем интерфейс, общий для каждого модуля и главного приложениия.
Интерфейс будет следующим:
В .NET написание плагинов является простой задачей, которая решается с помощью рефлексии (reflection). Рефлексия позволяет динамически загружать сборки, получать информацию о методах, свойствах, событиях и полях классов из сборок, создавать новые типы и вызывать методы во время выполнения. Классы и интерфейсы для рефлексии находятся в пространстве имен System.Reflection.
Для начала напишем два dll-модуля, в классах которых реализуем интерфейс, общий для каждого модуля и главного приложениия.
Интерфейс будет следующим:
-
- namespace Interface
- {
- public interface Interface1
- {
- int someMethod(int a, int b);
- string name { get; }
- }
- }
* This source code was highlighted with Source Code Highlighter.
Интерфейс положим в Interface.dll. Эта библиотека будет общей для модулей и основного приложения. В нашем случае, модуль будет работоспособен, если в конечном итоге dll-ка модуля будет лежать в каталоге с Interdace.dll.
Каждый модуль будет предполагать реализацию метода someMethod и свойства name. Свойство будет возвращать имя модуля.
Класс каждого плагина (модуля) будет реализовывать интерфейс из Interdace.dll, которую необходимо добавить в референсы плагинов. Разрабатывая плагин, мы имеем лишь интерфейс, необходимый для реализации.
Модуль 1
- using System;
- using System.Collections.Generic;
- using System.Text;
- using Interface;
-
- namespace Plugin1
- {
- public class pl1:Interface1
- {
- private string pl_name = "Произведение чисел";
- public int someMethod(int a, int b)
- {
- return a * b;
- }
- public string name
- {
- get { return pl_name; }
- }
- public static string getStaticString()
- {
- return "I'm a string from static method of plugin 1 which has been called using invoke method";
- }
-
- }
- }
* This source code was highlighted with Source Code Highlighter.
Модуль 2
- using System;
- using System.Collections.Generic;
- using System.Text;
- using Interface;
-
- namespace Plugin2
- {
- public class pl2 : Interface1
- {
- private string pl_name = "Сумма чисел";
- public int someMethod(int a, int b)
- {
- return a + b;
- }
- public string name
- {
- get { return pl_name; }
- }
-
- public string getNoneStaticString2()
- {
- return "I'm a string from none static method of plugin 2 which has been called using invoke method";
- }
-
- }
- }
* This source code was highlighted with Source Code Highlighter.
Итак, модули готовы. Приступим к написанию основного приложения MainApp. MainApp должно отслеживать появление новых модулей в определенной директории, подключение таковых, создание объектов классов модулей и приведение их к типу интерфейса (так же доступного в MainApp).
Поиск:
- public void loadPlugins()
- {
-
- string[] files = Directory.GetFiles(folder, "*.dll");
- foreach (string file in files)
- {
- try
- {
- Assembly assembly = Assembly.LoadFile(file);
- foreach (Type type in assembly.GetTypes())
- {
- Type iface = type.GetInterface("Interface.Interface1");
-
-
- if (null != iface)
- {
- Interface.Interface1 obj = (Interface.Interface1)Activator.CreateInstance(type);
- plugins.Add(obj);
- }
- }
- }
- catch (Exception e)
- {
- }
- }
-
- }
* This source code was highlighted with Source Code Highlighter.
Здесь путь к директории с плагинами декларирован так:
- private readonly string folder = Directory.GetCurrentDirectory() + "\\dlls\\";
* This source code was highlighted with Source Code Highlighter.
С помощью статичного метода LoadFile класса Assembly мы загрузили сборку, передав в метод название сборки. Класс Activator — это специальный класс для создания экземпляров типов и получения ссылок на удаленные объекты. С помощью метода CreateInstance класса Activator мы создали объект класса модуля, передав в метод тип класса оздаваемого объекта.
Вызывая loadPlugins() при запуске приложения, мы динамически подгрузим все модули, находящиеся в директории dlls, у которых классы реализуют нужный интерфейс.
Далее дело за вызовом методов. По какому-либо условию выбираем модуль, у которого собираемся вызвать метод.
- foreach (object pl in plugins)
- {
- if (((Interface.Interface1)pl).name.Equals(selected))
- {
- result = ((Interface.Interface1)pl).someMethod(a, b);
- }
- }
* This source code was highlighted with Source Code Highlighter.
Здесь условие выбора представляло собой комбобокс. Соответственно запускается Plugin1.pl1.someMethod() или Plugin2.pl2.someMethod().
А как же быть со статичными методами? Для работы с ними интерфейс помочь не может. Будем делать Invoke() метода, передавая имя.
- Assembly asm = Assembly.LoadFile(folder + "\\Plugin1.dll");
- Type classType = asm.GetType("Plugin1.pl1");
- MethodInfo mi = classType.GetMethod("getStaticString");
-
- label2.Visible = true;
- label2.Text = mi.Invoke(null, null).ToString();
* This source code was highlighted with Source Code Highlighter.
Так вызовется статичный метод у класса плагина_2. Заметьте, первым параметром Invoke() мы передали null.
Для того, чтобы с помощью Invoke вызвать нестатичный метод, необходимо лишь передать объект, содержащий метод.
- Assembly asm = Assembly.LoadFile(folder + "\\Plugin2.dll");
- Type classType = asm.GetType("Plugin2.pl2");
- object obj = Activator.CreateInstance(classType);
- MethodInfo mi = classType.GetMethod("getNoneStaticString2");
-
- label3.Visible = true;
- label3.Text = mi.Invoke(obj, null).ToString();
* This source code was highlighted with Source Code Highlighter.
Итак, мы динамически загружали сборки, создавали новые типы и вызывали методы (в том числе и статические) во время выполнения — одним словом, попробовали на вкус рефлексию.
Исходники лежат здесь
P.S. Очень надеюсь, что эта статья найдёт свою аудиторию и будет полезна