【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/注册表/扫码)

相关推荐
上海广测检测科技有限公司12 小时前
蓝牙音响TISI认证详解:泰国强制安全路径与多形态产品的合规分层判定
经验分享
我命由我1234512 小时前
金融租赁极简理解
经验分享·学习·职场和发展·金融·求职招聘·职场发展·学习方法
进击的大海贼12 小时前
基于C#开发的久坐提醒工具
开发语言·c#
小HANN13 小时前
华为云企业网站上云实战|从零搭建高可用WordPress(ECS+RDS+ELB+弹性伸缩+云监控全流程落地)
linux·运维·服务器·经验分享
FLJwu13 小时前
运动相机按键与连接器硬件选型实战02:USB‑C接口选型|防水失效、虚接断连、腐蚀烧口量产大坑
c语言·开发语言·经验分享·数码相机·相机·运动相机·oem 量产
fj800j13 小时前
首助记账本新增「标签管理」功能:跨分类归集项目账目,一个账本管完所有事
数据结构·经验分享·笔记·学习·分布式账本
小羊没烦恼!14 小时前
关于大型asp.net应用系统的架构-架构的选择
java·服务器·开发语言·前端·c#
爱吃奥利奥_wen15 小时前
排序算法通关手册:从 O(n²) 到 O(n log n),用一篇文章打通算法内功心法
开发语言·经验分享
我命由我1234516 小时前
单利计息与复利计息
经验分享·学习·职场和发展·产品运营·求职招聘·职场发展·产品经理