среда, 19 марта 2014 г.

Делаем плагины в программах на .Net

В .Net есть два варианта того, как сделать свои плагины: использование Microsoft Enterprise Library и использование отражений (reflection). Первый способ не совсем тривиальный и дня него нужно дополнительно скачивать MEL. Второй же способ доступен "из коробки" и, в большинстве ситуаций, его возможностей вполне достаточно.
Вообще, механизм отражений в .net - вещь достаточно мощная и интересная, так что, в будущем расскажу про него еще что-нибудь интересное. А пока переходим к плагинам.

Так как основа нашего механизма плагинов - отражение, то нам нужен какой-то базовый класс, который мы будет создавать при загрузке плагина. Так как кроме сигнатур предполагаемых методов с нашей стороны ничего больше не нужно, то идеальный вариант - создать интерфейс, поместив его в отдельную сборку (которую потом можно отдать сторонним разработчикам, которые будут делать плагины):

    
    public interface IPluginDef
    {
        string SayHello();
    }

Итак, что наследуя ваш интерфейс, насоздавали классов, наприсылали вам DLL'ек c ними и надо с этип добром теперь что-то делать. Для начала объявим где-нибудь в вашей программе глобальную переменную, в которой будут жить загруженные классы плагинов:
    
  List< IPluginDef > all_plugins = new List< IPluginDef >(); 



А теперь самое интересное. Пишем метод загрузки плагина:
        
        public bool LoadPlugin(string filePath)
        {
           if (!File.Exists(filePath)) return false;          
           Assembly PluginAssembly = null;
           try 
           {
              PluginAssembly = Assembly.LoadFrom(filePath);
           }
           catch
           {
               //сборка может быть не загружена по разным причинам, 
               //подробнее смотрите на MSDN: http://msdn.microsoft.com/ru-ru/library/1009fa28(v=vs.110).aspx
               return false;
           }
                
           //В одной сборке может быть объявлено несколько типов.
           Type[] allTypes = PluginAssembly.GetExportedTypes();
           foreach (Type t in allTypes)
           {
              //Если t - класс и, в нашем случае, наследует IPluginDef
              if (t.IsClass && typeof(IPluginDef).IsAssignableFrom(t))
              {
                  try 
                  {
                     //создаем класс
                     IPluginDef plugin = (IPluginDef)Activator.CreateInstance(t);
                     //и добавляем его в коллекцию плагинов
                     all_plugins.Add(plugin);
                  }
                  catch (MissingMethodException ex) 
                  {
                      //Исключение может быть выброшено, если у типа нет конструктора по умолчанию
                      //на самом деле, CreateInstance умеет создавать классы и из конструкторов с параметрами
                      //а с помощью отражения можно узнать какие конструкторы есть в классе, 
                      //но это уже тема для отдельного поста
                  }                           
              }
           }
           return true;
        }


Вот, в общем-то, и все. Теперь мы можем попросить все загруженные плагины поздороваться ;)
     
     foreach (IPluginDef plugin in all_plugins)
     {
       plugin.SayHello();
     }

Комментариев нет:

Отправить комментарий