【Hi.Ltd 专题】第8期:Managements 插件、单例与服务定位

Hi.Ltd 专题第 8 期 |模块:Managements |NuGet:Hi.Ltd 2026.7.11.1437

类型:PluginManager / PluginFactory / PluginFileInfo / Singleton<T> / LocateFactory / DongleValidationCache

场景:驱动插件热插拔、设备服务单例、HMI 本地化。

第7期:Threading 采集线程、锁与 LRU 缓存


本期目标

  1. 用 PluginFactory.Initialize / PluginManager 加载驱动插件目录
  2. 用 Invoke / InvokeClass / InvokeStatic / InvokeInstance 调用插件(含泛型、缓存)
  3. 用 Singleton<T>.Instance / GetInstance / TryGetInstance 管理全局服务,处理校验缓存与热插拔 ForceRevalidate
  4. 用 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>();
    }
}

可能出现的问题(现场坑)

  1. 依赖 DLL FileNotFound → GetDependenciesDirectory(Plugin\Dependencies)+ RegisterAssemblyResolve。 当前使用的插件类库的依赖库必做同步拷贝到Plugin\Dependencies此目录下
  2. 热更新顺序混乱 → 先 CheckPluginFileChanged,再 UnloadPlugin,再清缓存,再加载。
  3. Singleton + 加密狗抖 → 调 DongleValidationCacheDuration;热插拔用 ForceRevalidate。
  4. Invoke 参数类型不匹配 → CanConvertType/ConvertValue;注意缓存键含参数类型。
  5. 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/注册表/扫码)

相关推荐
光视界1 小时前
具身数采专项对比:ZED 双目相机 VS Intel RealSense,机器人数据集采集谁更合适?
经验分享·机器人
总线通信小百科11 小时前
车载以太网模块、交换机与媒体转换器:三类设备怎么选?
经验分享·车载以太网
2601_9672127214 小时前
安全型轨道式插座合规资质核查与工程选型技术要点
大数据·经验分享·安全
一只代码猿17 小时前
Screen Studio 免费邪修用法:剪视频完全无痛(附提示词)
经验分享
蒋胜山17 小时前
VBScript007-Excel的99乘法表
经验分享·batch
德思特17 小时前
实操指南|从“能驻网“到“能上网“:手机入网与公网业务实验室测试分享
经验分享·无线通信
览讯冷却19 小时前
冷却塔噪声的健康危害与法规要求的技术梳理
经验分享·笔记·科技·其他·百度·制造·业界资讯
学页技术极客19 小时前
「Skill市场」会重演「App Store」的故事吗
经验分享
恒锐丰科技林技术员20 小时前
EG2304L 高压半桥驱动芯片:中小功率电力电子方案优选器件
经验分享·嵌入式硬件·硬件工程
哎写bug的程序员21 小时前
关于github
经验分享