Pull to refresh

Позднее связывание в C#

Reading time6 min
Views8K
Вы видели хоть один проект на C# не использующий DLL? А серьезный не модульный проект? В любом случае рано или поздно для Вас данный вопрос будет критичным. Для остальных данная статья не представляет интереса, т.к. здесь описаны лишь базовые понятия с примером реализации рефлексии.

В .NET написание плагинов является простой задачей, которая решается с помощью рефлексии (reflection). Рефлексия позволяет динамически загружать сборки, получать информацию о методах, свойствах, событиях и полях классов из сборок, создавать новые типы и вызывать методы во время выполнения. Классы и интерфейсы для рефлексии находятся в пространстве имен System.Reflection.
Для начала напишем два dll-модуля, в классах которых реализуем интерфейс, общий для каждого модуля и главного приложениия.
Интерфейс будет следующим:
  1.  
  2. namespace Interface
  3. {
  4.   public interface Interface1
  5.   {
  6.     int someMethod(int a, int b);
  7.     string name { get; }
  8.   }
  9. }
* This source code was highlighted with Source Code Highlighter.


Интерфейс положим в Interface.dll. Эта библиотека будет общей для модулей и основного приложения. В нашем случае, модуль будет работоспособен, если в конечном итоге dll-ка модуля будет лежать в каталоге с Interdace.dll.


Каждый модуль будет предполагать реализацию метода someMethod и свойства name. Свойство будет возвращать имя модуля.
Класс каждого плагина (модуля) будет реализовывать интерфейс из Interdace.dll, которую необходимо добавить в референсы плагинов. Разрабатывая плагин, мы имеем лишь интерфейс, необходимый для реализации.

Модуль 1
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using Interface;
  5.  
  6. namespace Plugin1
  7. {
  8.   public class pl1:Interface1
  9.   {
  10.     private string pl_name = "Произведение чисел";
  11.     public int someMethod(int a, int b)
  12.     {
  13.       return a * b;
  14.     }
  15.     public string name
  16.     {
  17.       get { return pl_name; }
  18.     }
  19.     public static string getStaticString()
  20.     {
  21.       return "I'm a string from static method of plugin 1 which has been called using invoke method";
  22.     }
  23.       
  24.   }
  25. }
* This source code was highlighted with Source Code Highlighter.



Модуль 2
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using Interface;
  5.  
  6. namespace Plugin2
  7. {
  8.   public class pl2 : Interface1
  9.   {
  10.     private string pl_name = "Сумма чисел";
  11.     public int someMethod(int a, int b)
  12.     {
  13.       return a + b;
  14.     }
  15.     public string name
  16.     {
  17.       get { return pl_name; }
  18.     }
  19.  
  20.     public string getNoneStaticString2()
  21.     {
  22.       return "I'm a string from none static method of plugin 2 which has been called using invoke method";
  23.     }
  24.  
  25.   }
  26. }
* This source code was highlighted with Source Code Highlighter.



Итак, модули готовы. Приступим к написанию основного приложения MainApp. MainApp должно отслеживать появление новых модулей в определенной директории, подключение таковых, создание объектов классов модулей и приведение их к типу интерфейса (так же доступного в MainApp).
Поиск:
  1.     public void loadPlugins()
  2.     {
  3.  
  4.       string[] files = Directory.GetFiles(folder, "*.dll");
  5.       foreach (string file in files)
  6.       {
  7.         try
  8.         {
  9.           Assembly assembly = Assembly.LoadFile(file);
  10.           foreach (Type type in assembly.GetTypes())
  11.           {
  12.             Type iface = type.GetInterface("Interface.Interface1");
  13.  
  14.  
  15.             if (null != iface)
  16.             {
  17.               Interface.Interface1 obj = (Interface.Interface1)Activator.CreateInstance(type);
  18.               plugins.Add(obj);
  19.             }
  20.           }
  21.         }
  22.         catch (Exception e)
  23.         {
  24.         }
  25.       }
  26.  
  27.     }
* This source code was highlighted with Source Code Highlighter.


Здесь путь к директории с плагинами декларирован так:
  1. private readonly string folder = Directory.GetCurrentDirectory() + "\\dlls\\";
* This source code was highlighted with Source Code Highlighter.


С помощью статичного метода LoadFile класса Assembly мы загрузили сборку, передав в метод название сборки. Класс Activator — это специальный класс для создания экземпляров типов и получения ссылок на удаленные объекты. С помощью метода CreateInstance класса Activator мы создали объект класса модуля, передав в метод тип класса оздаваемого объекта.
Вызывая loadPlugins() при запуске приложения, мы динамически подгрузим все модули, находящиеся в директории dlls, у которых классы реализуют нужный интерфейс.

Далее дело за вызовом методов. По какому-либо условию выбираем модуль, у которого собираемся вызвать метод.
  1.         foreach (object pl in plugins)
  2.         {
  3.           if (((Interface.Interface1)pl).name.Equals(selected))
  4.           {
  5.             result = ((Interface.Interface1)pl).someMethod(a, b);
  6.           }
  7.         }
* This source code was highlighted with Source Code Highlighter.


Здесь условие выбора представляло собой комбобокс. Соответственно запускается Plugin1.pl1.someMethod() или Plugin2.pl2.someMethod().

А как же быть со статичными методами? Для работы с ними интерфейс помочь не может. Будем делать Invoke() метода, передавая имя.
  1.       Assembly asm = Assembly.LoadFile(folder + "\\Plugin1.dll");
  2.       Type classType = asm.GetType("Plugin1.pl1");
  3.       MethodInfo mi = classType.GetMethod("getStaticString");
  4.  
  5.       label2.Visible = true;
  6.       label2.Text = mi.Invoke(null, null).ToString();
* This source code was highlighted with Source Code Highlighter.


Так вызовется статичный метод у класса плагина_2. Заметьте, первым параметром Invoke() мы передали null.

Для того, чтобы с помощью Invoke вызвать нестатичный метод, необходимо лишь передать объект, содержащий метод.
  1. Assembly asm = Assembly.LoadFile(folder + "\\Plugin2.dll");
  2.       Type classType = asm.GetType("Plugin2.pl2");
  3.       object obj = Activator.CreateInstance(classType);
  4.       MethodInfo mi = classType.GetMethod("getNoneStaticString2");
  5.  
  6.       label3.Visible = true;
  7.       label3.Text = mi.Invoke(obj, null).ToString();
* This source code was highlighted with Source Code Highlighter.


Итак, мы динамически загружали сборки, создавали новые типы и вызывали методы (в том числе и статические) во время выполнения — одним словом, попробовали на вкус рефлексию.
Исходники лежат здесь

P.S. Очень надеюсь, что эта статья найдёт свою аудиторию и будет полезна
Tags:
Hubs:
+6
Comments7

Articles

Change theme settings