Цель Plugin.NET — создание гибкого менеджера плагинов на C#. Менеджер использует технологию рефлексии для динамической загрузки DLL-библиотек (плагины). При использовании менеджера требуется передать абстрактный класс в качестве параметра типа, а плагины должны реализовать этот интерфейс. Приложение затем вызывает методы этого интерфейса для взаимодействия с плагинами.
Проект активно развивается в свободное время после работы, поэтому прогресс может быть медленным. Добро пожаловать к участию в PR!
![]()
![]()
Проект написан на .NET Framework Yöntem 4.0, но можно легко адаптировать его для использования с .NET 2.0 и .NET Core.
Интерфейсы представляют собой абстрактные классы, содержащие обязательные и необязательные методы. Обязательные методы помечаются как abstract
, а необязательные — как virtual
. Пример ниже:
/// <summary>
/// Интерфейс плагина
/// </summary>
public abstract class PluginInterface
{
/// <summary>
/// Название плагина
/// </summary>
public abstract string Name { get; }
/// <summary>
/// Версия плагина
/// </summary>
public abstract Version Version { get; }
/// <summary>
/// Описание плагина
/// </summary>
public virtual string Description { get; }
/// <summary>
/// Вызывается при загрузке плагина, обязательно должно быть реализовано
/// </summary>
public abstract void Load();
/// <summary>
/// Первый метод функциональности, необязательный метод
/// </summary>
/// <returns></returns>
public virtual string Method1()
{
return null;
}
/// <summary>
/// Второй метод функциональности, необязательный метод
/// </summary>
/// <param name="from"></param>
/// <returns></returns>
public virtual string Method2(string from)
{
return from;
}
}
Использование
virtual
позволяет удобно обновлять интерфейсы без необходимости изменения всех плагинов.
Проект плагина должен ссылаться на библиотеку с интерфейсом. При выпуске плагина библиотека интерфейса не должна выпускаться, но все остальные зависимости должны быть включены. Менеджер будет искать классы, наследующие интерфейс, и создаст экземпляр первого найденного класса без параметров.
Важно отметить, что менеджер будет использовать только первый найденный класс, который реализует интерфейс. Другие классы будут игнорированы.
Load()
, чтобы загрузить все существующие плагины, возможно, передав функцию-фильтр.Watch()
для мониторинга новых плагинов в директории.StopWatch()
.class Program
{
static void Main(string[] args)
{
// Используйте интерфейс для создания экземпляра менеджера плагинов
// Если вам нужны другие интерфейсы, вы можете создать еще один экземпляр менеджера плагинов
var pluginManager = new PluginManager<PluginInterface>();
// Обрабатывайте события, отправляемые менеджером плагинов
pluginManager.OnAssemblyLoading += PluginManager_OnAssemblyLoading;
pluginManager.OnAssemblyLoaded += PluginManager_OnAssemblyLoaded;
pluginManager.OnError += PluginManager_OnError;
pluginManager.OnInstanceCreating += PluginManager_OnInstanceCreating;
pluginManager.OnInstanceCreated += PluginManager_OnInstanceCreated;
// Загружаем все плагины в директории
pluginManager.Load();
// Начинаем мониторить новые плагины, добавленные в директорию
pluginManager.Watch();
Console.WriteLine("Мониторинг изменений в директории плагинов, нажмите Enter для выхода");
Console.ReadLine();
}
private static void PluginManager_OnAssemblyLoading(object sender, PluginAssemblyLoadingArgs e)
{
Console.WriteLine($"Подготовка к загрузке сборки из файла \"{e.FileName}\"");
}
private static void PluginManager_OnAssemblyLoaded(object sender, PluginAssemblyLoadedArgs e)
{
Console.WriteLine($"Успешная загрузка сборки из файла \"{e.FileName}\": \"{e.Assembly}\"");
}
private static void PluginManager_OnInstanceCreating(object sender, PluginInstanceCreatingArgs e)
{
Console.WriteLine($"Подготовка к загрузке типа \"{e.Class}\" из сборки \"{e.Assembly}\"");
}
private static void PluginManager_OnInstanceCreated(object sender, PluginInstanceCreatedArgs<PluginInterface> e)
{
var plugin = e.Instance;
Console.WriteLine($"Успешная загрузка типа \"{e.Class}\" из сборки \"{e.Assembly}\"");
Console.WriteLine($"Название плагина: {plugin.Name}, версия: {plugin.Version}");
e.Instance.Load();
Console.WriteLine("Method1:" + plugin.Method1());
Console.WriteLine("Method2:" + plugin.Method2("啊呀哟"));
}
private static void PluginManager_OnError(object sender, PluginErrorEventArgs e)
{
switch (e.ErrorType)
{
case PluginNET.error.PluginErrorTypes.None:
break;
case PluginNET.error.PluginErrorTypes.InvalidDll:
Console.WriteLine($"Файл \"{e.FileName}\" не является действительной DLL: {e.Exception}");
break;
case PluginNET.error.PluginErrorTypes.CannotLoadClassTypes:
Console.WriteLine($"Не удалось загрузить типы из файла \"{e.FileName}\"");
break;
case PluginNET.error.PluginErrorTypes.ImplementionClassNotFound:
Console.WriteLine($"В файле \"{e.FileName}\" не найдено ни одного класса, реализующего указанный интерфейс");
break;
case PluginNET.error.PluginErrorTypes.IllegalClassDefinition:
Console.WriteLine($"В файле \"{e.FileName}\" найден класс, реализующий указанный интерфейс, но он не объявлен как класс или объявлен как абстрактный или не является публичным");
break;
case PluginNET.error.PluginErrorTypes.DefaultConstructorNotFound:
Console.WriteLine($"В файле \"{e.FileName}\" найден класс, реализующий указанный интерфейс, но не найден конструктор без параметров");
break;
case PluginNET.error.PluginErrorTypes.InstanceCreateFailed:
Console.WriteLine($"В файле \"{e.FileName}\" найден класс, реализующий указанный интерфейс \"{e.ClassType}\", но возникло исключение при создании экземпляра: {e.Exception}");
break;
case PluginNET.error.PluginErrorTypes.Unknown:
Console.WriteLine("Неизвестная ошибка");
break;
default:
break;
}
}
}
Примеры можно найти в каталоге test решения, тестовый проект — Plugin.NETTest.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )