C# Avalonia如何实现一个插件系统 Avalonia MEF/DI插件

来源:这里教程网 时间:2026-02-21 17:36:22 作者:

在 Avalonia 中实现插件系统,推荐使用 Microsoft.Extensions.DependencyInjection(DI) 结合 MEF(Managed Extensibility Framework) 或纯 DI 方式动态加载插件。Avalonia 本身不内置插件机制,但可借助 .NET 的模块化能力(如 AssemblyLoadContext、AssemblyLoadEventArgs、接口抽象 + 运行时反射)构建松耦合、热插拔的插件架构。

定义统一插件接口与契约

所有插件必须实现约定接口,这是解耦核心。建议放在独立类库(如

MyApp.Plugins.Contracts
)中供宿主和插件共同引用:

IPlugin:基础生命周期(
Initialize()
/
Shutdown()
INavigationProvider:提供菜单项或导航入口(返回
MenuItem
Route
IViewComponent:可被
ContentControl
动态渲染的 Avalonia 控件(继承自
Control
避免在接口中引用 Avalonia 程序集(如
Avalonia.Controls.Button
),改用抽象类型或数据模型

插件发现与动态加载(基于 AssemblyLoadContext)

不依赖 MEF 的轻量方案(更可控、兼容 .NET 6+):

插件以
.dll
形式存放于
Plugins/
目录,命名规范如
MyPlugin.dll
创建隔离的
AssemblyLoadContext
防止类型冲突:
var pluginContext = new AssemblyLoadContext(isCollectible: true);
var assembly = pluginContext.LoadFromAssemblyPath(pluginPath);
遍历
assembly.GetTypes()
,筛选实现
IPlugin
的类型,用
Activator.CreateInstance
创建实例
调用
plugin.Initialize(services)
,将宿主的
IServiceCollection
传入,让插件注册自身服务(如
services.AddSingleton<iexportservice pluginexportservice>()</iexportservice>

宿主 DI 容器集成插件服务

AppBuilder
构建阶段注入插件:

先构建基础
IServiceCollection
(含 Avalonia 默认服务)
扫描并加载插件,执行其
Initialize(IServiceCollection)
完成所有插件注册后,调用
BuildServiceProvider()
关键点:插件内部应只注册服务,**不调用
BuildServiceProvider
**,避免容器嵌套

示例片段:

var services = new ServiceCollection();
// 注册宿主服务...
RegisterHostServices(services);
// 加载插件
foreach (var pluginPath in GetPluginPaths())
{
    var plugin = LoadPlugin(pluginPath);
    plugin.Initialize(services); // 插件向 services 添加自己的类型
}
var app = BuildAvaloniaApp()
    .UsePlatformDetect()
    .SetupWithLifetime(lifetime);
app.StartWithClassicDesktopLifetime(args, ShutdownMode.OnMainWindowClose);

运行时 UI 扩展(菜单/视图/命令)

插件通过接口向宿主“声明能力”,宿主负责聚合与呈现:

插件实现
INavigationProvider.GetMenuItems()
→ 宿主收集所有
MenuItem
并添加到主菜单
插件实现
IViewComponent.CreateView()
→ 宿主用
<contentcontrol content="{Binding ActivePluginView}"></contentcontrol>
绑定渲染
命令可绑定到
ICommand
属性,由插件提供 ViewModel 实现,宿主仅负责触发
避免直接在插件中操作宿主窗口(如
Application.Current.MainWindow
),改用事件或消息总线(如
WeakEvent
CommunityToolkit.Mvvm.Messaging
)通信

不复杂但容易忽略:确保插件 DLL 不包含重复依赖(如 Avalonia.*),全部由宿主提供;发布时将插件目录设为

CopyToOutputDirectory
;调试阶段可用
AssemblyResolve
事件辅助定位加载失败原因。

相关推荐

热文推荐