【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
狗校验 DongleValidationCacheDurationClearDongleValidationCache*ClearAllDongleValidationCacheForceRevalidate
诊断 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 FileNotFoundGetDependenciesDirectory(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/注册表/扫码)

相关推荐
混凝土拌意大利面2 小时前
分类量化定价法
经验分享
德思特11 小时前
Robotaxi规模化加速,“信号难题”如何解决?
经验分享
曹牧12 小时前
C#:24小时制时间
c#
统计学小王子14 小时前
数学建模十万个为什么(一)
经验分享·数学建模
陕西企来客15 小时前
企来客科技发布企业知识图谱GEO优化专项服务:从“信息曝光“升级到“实体置信度“
经验分享·ai
CAYA-16 小时前
模电笔记1.1 半导体基础与 PN 结
经验分享·笔记·学习方法
唐青枫17 小时前
别急着拆服务:C#.NET 微服务架构从边界设计到实战
c#·.net
易知微EasyV数据可视化18 小时前
大型工厂/港口/城市级孪生构建:多源三维数据如何实现高效协同?
经验分享
一川风絮千片雪18 小时前
【环境配置】Ubuntu 24.04 升级新内核后 Kernel Panic:NVIDIA DKMS 编译失败导致 initramfs 缺失的修复过程
经验分享