#c# #plugins
#c# #Плагины
Вопрос:
По сути, мне нужна возможность добавлять новые функции в мое приложение, не обновляя само приложение.
Допустим, у меня есть приложение с двумя плагинами.
Приложение имеет функцию с именем generateNumber();
Plugin_1 будет иметь следующий код:
void init(){ }
int exec(){
int number = HOST_APPLICATION.generateNumber();
number = number * 2;
return number;
}
Plugin_2 будет иметь ту же структуру, только другую функциональность:
void init(){ }
int exec(){
int number = HOST_APPLICATION.generateNumber();
number = (number * 10) 13;
return number;
}
Мне нужно, чтобы несколько экземпляров каждого «плагина» запускались одновременно (каждый в своем потоке), поэтому одновременно всего 20 потоков. Код будет примерно таким:
Основное приложение:
void init_plugins() { }
void execute_plugins(){
for(int i=0; i<plugins.count; i ){
for(int z=0; z<10; z ){
Thread t = new Thread(plugin_research);
t.start(plugins[i]);
}
}
}
void plugin_research(PluginStructure Plugin){
Plugin.init();
int return_val = Plugin.exec();
// do something with return_val
}
Мне также нужно, чтобы хост-приложение вызывало функции плагина (структура будет одинаковой для всех плагинов), а плагин мог вызывать функции хост-приложения.
У каждого плагина будут разные элементы управления для настройки. Мне нужно было бы показать конфигурацию в одном месте, но вызывать функции плагина несколько раз одновременно (с использованием потоков)
Возможно ли это? Каков наилучший способ добиться этого? могу ли я сделать это с помощью IPlugin?
Ответ №1:
Посмотрите на MEF, платформу управляемой расширяемости на http://mef.codeplex.com /. В нем много встроенной поддержки для обнаружения компонентов плагина во время выполнения.
Ответ №2:
В основном каждый плагин должен быть производным от базового класса, предоставляющего нужные вам методы.
public abstract class BasePlugin
{
protected int number;
public abstract void init_plugin();
public abstract int exec(int number);
public BasePlugin(int number)
{
this.number = number;
}
}
Тогда у вас есть
public class Plugin1: BasePlugin
{
public override void init_plugin()
{
}
public override int exec(int number)
{
}
}
В вашем приложении вы можете создавать плагины и сохранять их в списке
List<BasePlugin> list = new List<BasePlugin>();
list.Add(new Plugin1(generateNumber()));
BasePlugin p2 = new Plugin2(generateNumber());
p2.init_plugin();
list.Add(p2);
и делайте с загруженными плагинами все, что вам заблагорассудится.
Или (как я вижу из вашего отредактированного вопроса) вы можете создавать потоки для каждого плагина…
Для загрузки плагинов вы можете использовать такие функции, как:
public static List<T> GetFilePlugins<T>(string filename)
{
List<T> ret = new List<T>();
if (File.Exists(filename))
{
Type typeT = typeof(T);
Assembly ass = Assembly.LoadFrom(filename);
foreach (Type type in ass.GetTypes())
{
if (!type.IsClass || type.IsNotPublic) continue;
if (typeT.IsAssignableFrom(type))
{
T plugin = (T)Activator.CreateInstance(type);
ret.Add(plugin);
}
}
}
return ret;
}
public static List<T> GetDirectoryPlugins<T>(string dirname)
{
List<T> ret = new List<T>();
string[] dlls = Directory.GetFiles(dirname, "*.dll");
foreach (string dll in dlls)
{
List<T> dll_plugins = GetFilePlugins<T>(Path.GetFullPath(dll));
ret.AddRange(dll_plugins);
}
return ret;
}
Комментарии:
1. Спасибо, Марко! Можете ли вы сказать мне, когда я начну новый проект на C #, что я должен выбрать, выиграть приложение для плагина? Мне тоже нужен графический интерфейс для настройки. Каждый плагин будет иметь свою собственную конфигурацию.
2. @user1015551: для каждого плагина вы можете выбрать оконную форму; затем в свойствах проекта перейдите на вкладку приложения и выберите «Библиотека классов» в качестве типа вывода
3. @user1015551: как вы можете видеть из моего кода, не вызывайте
HOST_APPLICATION.generateNumber();
из плагинов, а передавайте новый сгенерированный номер каждому плагину с его конструктором. Таким образом, вы разделяете плагины из своего приложения .. в противном случае, в чем причина наличия отдельных плагинов?4. Спасибо! Еще один вопрос. какие функции я использую для загрузки библиотеки (плагина) из основного приложения? могу ли я создать графический интерфейс для библиотеки классов? или мой плагин должен получать доступ к основной форме приложения и генерировать графический интерфейс с помощью кода?
5. Причина, по которой я хочу вызвать generateNumber() из плагина, заключается в том, что generateNumber() на самом деле будет http-классом-оболочкой, чтобы я мог отслеживать запущенные потоки, использование сети и т. Д. В основном приложении. это плохой способ сделать это?
Ответ №3:
Проверьте MEF — http://msdn.microsoft.com/en-us/library/dd460648.aspx . Даже если вы решите не использовать его, вы можете увидеть, как такая архитектура может быть реализована и использована.
Ответ №4:
Загляните в MEF, платформу управляемой расширяемости.