生命周期
每一次状态迁移都由宿主驱动。管理器不会自己启动任何东西:LoadAll 是你要求把整套装上,下面这些按插件调用的方法则是其余部分。
manager.LoadAll(); // 按依赖顺序装载 plugins/ 下的全部manager.Load(packageDirectory); // 按路径装载单个包manager.Enable("Hello"); // 按清单名启用manager.Disable("Hello");manager.Unload("Hello");Load 与 Enable 返回 bool。Unload 返回带两个字段的 UnloadResult:
public readonly record struct UnloadResult(bool Released, string? Error);Released只在插件真的被回收时为 true。Error在没回收时带上原因。
卸载不是一步。管理器先放下实例与作用域,再等加载器确认程序集装载上下文已被回收。两段的失败方式不一样:
| 情况 | 结果 |
|---|---|
| 还有插件依赖它 | 拒绝,Released 为 false。先卸下游插件——管理器不做级联。 |
| 这个插件是已登记的加载器 | 拒绝;加载器不能在运行期更换。重启服务端。 |
| 插件拒绝了停用 | 拒绝;插件留在原处,可能仍在活跃。 |
| 已放下,但回收没被确认 | Released 为 false,Error 里写超时;插件停在 Detached。再调一次 Unload 重新验证。 |
最后一种值得记住。实例已经没了,但装载上下文被什么东西钉住——静态字段、还在跑的线程、OnUnload 里没清掉的引用。再调 Unload 只重跑验证,暂时被钉住的上下文第二次可能就过了。
UnloadVerificationTimeout 设定这段等待的预算,见开始内嵌。
Plugins 返回每个槽位的只读快照,包含已放下、等待回收的插件:
using Microsoft.Extensions.Logging;
var log = loggerFactory.CreateLogger("PluginManager");
foreach (var info in manager.Plugins){ log.LogInformation( "{Name} {Version} loader={Loader} state={State} enabled={Enabled}", info.Name, info.Version, info.Loader, info.State, info.Enabled);}TryGetPlugin("Hello", out var info) 按名字取一条;Detached 的条目也能找到。
PluginInfo 带清单名与版本、加载器 id、PluginState、逻辑上的 Enabled 标志、这个包是不是已登记的加载器,以及声明的依赖。每条依赖暴露 Name 与 VersionRange。
两个字段容易混:
PluginState.Loaded表示实例与作用域都在。PluginState.Detached表示它们已经放下、只等回收——插件仍在表里,但不在运行。Enabled是另一条轴。插件可以同时是已装载且已停用。
LoadedNames 是在场且为 Loaded 的插件名,短一些的那份清单。
快照不是什么
Section titled “快照不是什么”它是快照:拿着它不会钉住任何东西,它也不会反映之后的变化。要看当前状态就再查一次 Plugins。它也不交出插件实例——那要直接调管理器;句柄归管理器所有。
