Hi.Ltd 专题第 8 期 |模块:Managements |NuGet:
Hi.Ltd2026.7.11.1437类型:
PluginManager/PluginFactory/PluginFileInfo/Singleton<T>/LocateFactory/DongleValidationCache场景:驱动插件热插拔、设备服务单例、HMI 本地化。
本期目标
- 用
PluginFactory.Initialize/PluginManager加载驱动插件目录 - 用
Invoke/InvokeClass/InvokeStatic/InvokeInstance调用插件(含泛型、缓存) - 用
Singleton<T>.Instance/GetInstance/TryGetInstance管理全局服务,处理校验缓存与热插拔ForceRevalidate - 用
LocateFactory做多语言资源
适用范围
| 场景 | 说明 |
|---|---|
| 三菱/Modbus/视觉 DLL 作插件 | SetPluginDirectory + LoadPluginsFromDirectory |
| 通讯服务全局唯一 | Singleton<CommService>.Instance |
| 加密狗授权的商业模块 | IDongleValidated + RequireStrictDongleValidationAttribute + Fallback |
| HMI 中/英切换 | LocateFactory.InitializeLocalization / SetCulture |
不该用 / 慎用
| 场景 | 说明 |
|---|---|
| 生产热更不清理缓存 | 需 CheckPluginFileChanged + Unload* + Clear*Caches 顺序 |
| 插件依赖随便丢 | 配 Dependencies 目录 + RegisterAssemblyResolve |
插件:IPlugin 与加载
csharp
using Hi.Ltd.Interface;
using Hi.Ltd.Managements;
public sealed class MitPlcPlugin : IPlugin
{
public string Name => "MitPlc";
public string Description => "三菱驱动";
public bool Init(params object[] args) => true;
public object Execute(params object[] args) => null;
public object InvokeMethod(string methodName, params object[] args) => null;
}
推荐入口:PluginFactory
XML:PluginManager 偏内部实现,用户应通过 PluginFactory 使用。
csharp
// 设置目录(自动创建必要文件夹,含 Dependencies)
PluginFactory.SetPluginDirectory(@"D:\Plugins");
// 初始化:加载插件,可选预编译方法
PluginFactory.Initialize(
pluginDirectory: @"D:\Plugins",
precompileMethods: true,
pluginNames: null); // 或指定名称数组
PluginFactory.SetDefaultPlugin("MitPlc");
PluginFactory.SetDefaultClass("MitPlcDriver");
PluginManager 能力(需要细控时)
| 分组 | 方法 |
|---|---|
| 加载 | CreateInstance / LoadFrom / LoadPluginsFromDirectory / GetPluginAssembly / GetTypeFromPlugin / HasPlugin |
| 目录 | SetPluginDirectory / GetPluginDirectory / GetDependenciesDirectory |
| 调用 | InvokeStaticMethod / InvokeInstanceMethod / InvokeGenericInstanceMethod / InvokeMethodWithRefOut / InvokeStrongTyped* |
| 缓存 | GetCachedInstance / ClearInstanceCache / ClearAllCaches / CleanupPluginCaches / PrecompileMethods |
| 变更 | CheckPluginFileChanged / CheckAllPluginFilesChanged / CheckPluginFileExists |
| 卸载 | UnloadPlugin / UnloadAllPlugins |
| 解析 | RegisterAssemblyResolve / OnAssemblyResolve |
| 成员 | GetMemberValue / SetMemberValue / GetInstanceFromStaticMember |
PluginFactory 再包一层:Invoke/InvokeClass/InvokeStatic/InvokeGenericClass/InvokeInstance、默认插件/类名、Unload* 等。
csharp
// 统一入口:自动识别静态/实例
object raw = PluginFactory.Invoke("MitPlc", "MitPlcDriver", "ReadHolding", 1, 100, 10);
// 已设默认插件/类名时
object raw2 = PluginFactory.Invoke("ReadHolding", 1, 100, 10);
bool changed = PluginFactory.CheckPluginFileChanged("MitPlc");
if (changed)
{
PluginFactory.UnloadPlugin("MitPlc");
PluginFactory.ClearAllCaches();
// 再 Initialize / LoadFrom
}
注:此插件系统的目的是解耦合,功能块基于类库是否存在,如果存在就可以执行,如果不执行,当前功能缺失,但不影响整个软件的使用,可以根据需求灵活搭配功能
Singleton<T>
| 成员 | 说明 |
|---|---|
Instance |
无参默认实例 |
GetInstance(params object[] args) |
按参数键创建/获取 |
TryGetInstance / TryGetInstance(args) |
返回 Result,不抛 |
RemoveInstance / ClearInstanceCache |
生命周期;实现 IDisposable 会自动 Dispose |
| 狗校验 | DongleValidationCacheDuration、ClearDongleValidationCache*、ClearAllDongleValidationCache、ForceRevalidate |
| 诊断 | LogDiagnostic |
csharp
public sealed class CommService
{
public void Start() { /* 连 PLC */ }
}
var svc = Singleton<CommService>.Instance;
svc.Start();
var r = Singleton<CommService>.TryGetInstance();
if (!r.Successed) { /* 狗校验失败等 */ }
// 热插拔狗后重建
Singleton<CommService>.ForceRevalidate();
DongleValidationCache:独立缓存类型,与 Singleton 校验时长策略配合(避免每次访问都打狗)。
LocateFactory(本地化)
csharp
LocateFactory.InitializeLocalization(@"D:\Locale");
LocateFactory.AttachLocales(dict); // ConcurrentDictionary<string,string>
LocateFactory.SetCulture("zh-CN");
string tip = LocateFactory.GetValue("key", "default", args);
完整示例:加载驱动插件并读保持寄存器
csharp
using Hi.Ltd;
using Hi.Ltd.Managements;
public static class PlcPluginHost
{
public static Result Boot(string pluginDir)
{
PluginFactory.SetPluginDirectory(pluginDir);
PluginFactory.RegisterAssemblyResolve(); // 若 Factory 未自动注册,用 Manager
// PluginManager 侧:
var pm = PluginManager.CreateInstance();
pm.SetPluginDirectory(pluginDir);
pm.RegisterAssemblyResolve();
var loaded = pm.LoadPluginsFromDirectory();
if (!loaded.Successed) return loaded;
pm.PrecompileMethods(precompileStatic: true, precompileInstance: true, pluginNames: null);
if (!pm.HasPlugin("MitPlc"))
return Error.Argument(nameof(pluginDir), "未找到 MitPlc 插件");
PluginFactory.SetDefaultPlugin("MitPlc");
PluginFactory.SetDefaultClass("Driver");
return Result.Success; // 以实际 Success 工厂为准
}
public static short[] ReadHolding(byte station, ushort address, ushort count)
{
object raw = PluginFactory.Invoke("ReadHolding", station, address, count);
return raw as short[] ?? Array.Empty<short>();
}
}
可能出现的问题(现场坑)
- 依赖 DLL FileNotFound →
GetDependenciesDirectory(Plugin\Dependencies)+RegisterAssemblyResolve。 当前使用的插件类库的依赖库必做同步拷贝到Plugin\Dependencies此目录下 - 热更新顺序混乱 → 先
CheckPluginFileChanged,再UnloadPlugin,再清缓存,再加载。 - Singleton + 加密狗抖 → 调
DongleValidationCacheDuration;热插拔用ForceRevalidate。 - Invoke 参数类型不匹配 →
CanConvertType/ConvertValue;注意缓存键含参数类型。 - XML 写 PluginManager 内部 → 业务优先
PluginFactory。
异常点与应对
| 现象 | 应对 |
|---|---|
| LoadFrom 失败 | 路径、TFM、依赖、是否被锁 |
| Invoke 失败 | 类名/方法名;HasPlugin;看异常 Message |
| 狗校验失败 | Fallback 特性;TryGetInstance;勿在 UI 裸 Instance |
| 多实例泄漏 | RemoveInstance / ClearInstanceCache |
| 同名方法冲突 | Invoke/InvokeStatic 带类名重载 |
常见问题
Q: PluginManager 与 PluginFactory?
A: Factory 是推荐门面;Manager 更底层(XML 亦建议经 Factory)。
下篇预告
第9期:Interop(JSON/INI/XML/YAML/CSV/TOML/注册表/扫码)