Natasha 插件化之dll

这篇具有很好参考价值的文章主要介绍了Natasha 插件化之dll。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

调用外部dll来实现组件化

场景

  1. 有一个设备管理控制系统,主要作用是控制设备及收集相关设备的信息,目前只集成了门禁和监控,后期期望添加更多设备时,一般都是在公司编写完后现场实施并调试,代码一般也是每个设备创建独立的项目,供总项目调用;慢慢的可能会演变出所有设备都继承一个公共的接口类,接口类中实现获取能力集和发送命令,以此来减少对于总控模块的修改,而此时只需要维护好能力集即可;可是这样每次也需要运行一整个解决方案,如果其他地方也需要这个系统,只能通过卸载项目来减少引用进行实时,这时就可以通过将项目分拆出去,通过Natasha进行组件化的管理。这样子的好处是通过约定的接口和能力集进行通信,主程序和设备耦合度低,如果遇到其他项目需要该系统,只需要将所需的dll放入特定文件夹即可。
  2. 任何可以分拆成模块的系统都可以按照组件化的逻辑进行开发,例如有业务流程的项目,每个步骤可有多个组件选项,可执行一个或者多个;或者说通过获得的插件来动态配置那一步应执行那个插件,通过插件的反馈判断是否应执行下一步操作。

好处

  1. 低耦合,业务分拆,插件只需要关心插件接口及反馈即可,不需要关心核心系统的业务逻辑
  2. 分工明确,每个人只需关注插件代码即可,项目整合后出现问题也好排查,未调用插件,则主系统有问题,调用插件结果与实际不符则插件有问题。
  3. 维护方便,学习成本低,对于某一个特定插件,代码量会远远低于整个项目的代码量,而且每个业务都可以进行分拆。

实现

  1. 获得Assembly

主要使用NatashaDomain类实例化的方法(源码位置:src\Natasha.Domain\Extension\NatashaDomainExtension.cs)

相关方法:

LoadPluginUseDefaultDependency 如果加载的dll已经被加载过了,则跳过

LoadPluginWithAllDependency 不会判断高低版本,源码中的解释是默认的,感觉和Default类似

LoadPluginWithHighDependency 使用高版本的dll

LoadPluginWithLowDependency 使用低版本的dll

参数说明:

string path 必填,dll所在路径

Func<AssemblyName, bool>? excludeAssembliesFunc = null 选填,需要排除的dll,返回true为排除引用,例如共同引用某个公用的dll(例如Utils.dll),此时可以选择使用哪个版本的dll

  1. 找到clas类并且实例化

找到class类(默认所有插件的实现类为*Controller),也可以按照下面例子来

var type = assembly.GetTypes().Where(item => item.Name.IndexOf("Controller")!=-1).First();

实例化

var plugin = (IPluginClass)(Activator.CreateInstance(type)!);

例子

  1. 创建两个底层项目

    1. IPluginBase项目:用于创建插件使用的接口

      using PluginUtil;
      
      namespace IPluginBase
      {
          public interface IPluginClass
          {
              /// <summary>
              /// 初始化方法
              /// </summary>
              public void initialize();
              /// <summary>
              /// 获得插件特有的方法
              /// </summary>
              /// <returns></returns>
              public List<FruitFunction> getFunction();
              /// <summary>
              /// 获得需要定时方法
              /// </summary>
              /// <returns></returns>
              public List<FruitFunction> getTimeFunc();
              /// <summary>
              /// 通过方法执行插件代码
              /// </summary>
              /// <param name="function">FruitFunction</param>
              /// <param name="param">可能输入的数值</param>
              /// <returns></returns>
              public String execute(FruitFunction function, String param);
          }
      }
      
    2. PluginUtil项目,用于声明FruitFunction

      using System.ComponentModel;
      
      namespace PluginUtil
      {
          public enum FruitFunction
          {
              [Description("硬度")]
              hardness = 01,
              [Description("苹果特有功能")]
              appleAttr = 02,
              [Description("切")]
              cut = 03
          }
      }
      
  2. 创建两个插件项目

    1. PluginApple项目

      using IPluginBase;
      using PluginUtil;
      
      namespace PluginApple
      {
          public class PluginAppleClass : IPluginClass
          {
              public string execute(FruitFunction function, string param)
              {
                  switch (function) {
                      case FruitFunction.cut:
                          Console.WriteLine("切苹果");
                          break;
                      case FruitFunction.hardness: 
                          Console.WriteLine("苹果很脆");
                          break;
                      case FruitFunction.appleAttr:
                          Console.WriteLine("苹果特有属性");
                          break;
                  }
                  return "结束";
              }
      
              public List<FruitFunction> getFunction()
              {
                  Console.WriteLine("返回苹果的现有功能");
                  return new List<FruitFunction>() { FruitFunction.appleAttr, FruitFunction.cut, FruitFunction.hardness }; 
              }
      
              public List<FruitFunction> getTimeFunc()
              {
                  Console.WriteLine("返回苹果的定时功能");
                  return new List<FruitFunction>() { FruitFunction.cut};
              }
      
              public void initialize()
              {
                  Console.WriteLine("苹果初始化完成");
              }
          }
      }
      
    2. PluginBanana项目

      using IPluginBase;
      using PluginUtil;
      
      namespace PluginBanana
      {
          public class PluginBananaClass : IPluginClass
          {
              public string execute(FruitFunction function, string param)
              {
                  switch (function)
                  {
                      case FruitFunction.cut:
                          Console.WriteLine("切香蕉");
                          break;
                      case FruitFunction.hardness:
                          Console.WriteLine("香蕉很软");
                          break;
                  }
                  return "结束";
              }
      
              public List<FruitFunction> getFunction()
              {
                  Console.WriteLine("返回香蕉的现有功能");
                  return new List<FruitFunction>() { FruitFunction.cut, FruitFunction.hardness };
              }
      
              public List<FruitFunction> getTimeFunc()
              {
                  Console.WriteLine("返回香蕉的定时功能");
                  return new List<FruitFunction>() { FruitFunction.cut };
              }
      
              public void initialize()
              {
                  Console.WriteLine("香蕉初始化完成");
              }
          }
      }
      
  3. 使用Natasha实现两个插件项目的方法

    前置条件:将PluginApple.dll和PluginBanana.dll放到NatashaStudyConsole.exe同级下的plugins文件夹中

    using IPluginBase;
    using PluginUtil;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace NatashaStudyConsole
    {
        internal class PluginDemo
        {
            public void PluginMethod() {
                //获得dll存放路径
                string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory+ "plugins");
                //实例化NatashaDomain
                NatashaDomain domain = new(Guid.NewGuid().ToString());
                List<IPluginClass> assemblies = new List<IPluginClass>();
                //通过获得所有的dll来进行实例化
                Directory.GetFiles(path, "*.dll").ToList().ForEach(dll =>
                {
                    //加载dll
                    var assembly = domain.LoadPluginWithAllDependency(dll);
                    // 本例子中项目名称为"A",需要实例化的类为"AClass",因此使用IndexOf方法
                    // 获得项目名称
                    var asmName = assembly.GetName().Name!;
                    // 根据项目名称判断应该实例那个class
                    var type = assembly.GetTypes().Where(item => item.Name.IndexOf(asmName)!=-1).First();
                    // 实例化
                    var plugin = (IPluginClass)(Activator.CreateInstance(type)!);
                    if (plugin != null) {
                        // 保存
                        assemblies.Add(plugin);
                    }
                });
                assemblies.ForEach(assembly => {
                    // 实现相关方法
                    List<FruitFunction> functions = assembly.getFunction();
                    if(functions != null && functions.Count > 0)
                    {
                        assembly.execute(functions[0], "");
                    }
                });
            }
        }
    }
    
    

    代码结构示意图:

    Natasha 插件化之dll

    执行结果:

    Natasha 插件化之dll文章来源地址https://www.toymoban.com/news/detail-451733.html

到了这里,关于Natasha 插件化之dll的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • uniapp - 微信小程序接入腾讯视频播放器功能插件,uniapp开发微信小程序端调用引入并使用腾讯视频播放组件完整全流程(详细示例源码,一键复制开箱即用)

    在uniapp 微信小程序项目中,集成腾讯视频功能插件,实现播放腾讯视频效果,附带详细示例源码及注释, 你可以跟着步骤一步步来,保证几分钟就能快速在uniapp小程序项目中植入腾讯视频功能!

    2024年02月12日
    浏览(68)
  • React 中,forwardRef 和 useImperativeHandle 配合实现父组件调用子组件方法和属性

    React api 的用法简介:      forwardRef: React 提供的一个特殊的 API,主要用于将 ref 属性从父组件 “转发”(forward)到子组件。在 React 中,ref 主要用于获取组件或 DOM 元素的引用,这样我们就可以在需要的时候访问和操作这些组件或元素。然而,由于 React 的 props 传递机制

    2024年01月19日
    浏览(35)
  • 利用C#调用Toast通知来进行自定义通知实现及自定义组件调用

    阳春三月的开始就连续加班了两天,就因为这个小小的Toast通知的实现,具体踩的坑就列在下方方便大家查阅了!!! 这次的需求是需要利用本地的PC工具写入注册表,在PC启动时调用工程进行文件检查,从而弹窗提示是否进行操作。操作不通即需要对不同的选择进行不同的批

    2024年02月08日
    浏览(43)
  • 【Flutter跨平台插件开发】如何实现kotlin跟C++的相互调用

    在 Kotlin 中,可以使用 JNI (Java Native Interface) 来调用 C++ 代码 调用步骤: 创建 C++ 文件并实现函数。 在 Kotlin 中声明需要调用的 native 函数并加载 native 库。 调用示例 Flutter 插件项目的例子 在 Flutter 插件中引用已有的 C++ 源码需要以下步骤: 首先,在 Flutter 插件的 android 目录下

    2024年01月25日
    浏览(64)
  • Natasha相关辅助类 (六)

    获得相关类的访问级别 AccessReverser.GetAccess () 可以是泛型 AccessReverser.GetAccess方法中的参数可以是 属性名称 说明 PropertyInfo 获取属性的访问级别 MethodInfo 获取方法的访问级别 EventInfo 获取事件的访问级别 FieldInfo 获取字段的访问级别 Type 获取类型的访问级别 T 获取泛型的访问级

    2024年02月05日
    浏览(36)
  • vue3 使用 mitt 插件实现非父子组件传值

    介绍 : mitt 是一个 JavaScript 库,用于实现事件的订阅和发布 1、安装 2、新建 utils/eventBus.ts 文件 3、使用

    2024年02月09日
    浏览(49)
  • idea从插件市场或者外部插件库导入插件的方式

    我们平时在使用idea进行开发时,会用到一些插件来辅助开发或增强某些功能。选好合适的插件能帮助我们提高开发效率,事半功倍。 以下提供 从idea内部自带的插件市场下载插件 和 从idea外置插件库导入插件 的两种方式。后一种方式主要在插件市场无法联网或者某些插件在

    2024年01月17日
    浏览(43)
  • mssql调用外部接口

    断更很久了。 是因为这段时间发现,AI出来之后,很多博客都没有记录的必要了,你问他他都能即时告诉你。 这篇博客产出的原因是,看到一份奇葩需求,说数据库改某行数据的状态字段,也要调用接口。 我觉着挺奇葩的,就记录一下吧。 数据库触发器内,调用外部接口

    2024年02月07日
    浏览(43)
  • Unity | 调用外部exe

    加载控制台应用程序需要设置WorkingDirectory属性(猜测原因:我项目中加载的控制台应用程序启动时要调用别的dll,这个dll要加载本地模型文件,所以需要设置exe的当前工作目录),否则会加载失败。 如果第三方exe的路径是通过config.txt配置的,那么读取这个路径字符串时需要

    2024年02月15日
    浏览(27)
  • SpringBoot 调用外部接口

    一个系统肯定少不了要和外部系统进行通信,所以就必须得访问外部接口。 本次演示的是使用的是 高德天气api接口 使用插件方式,比如自带的HttpClient,或者OkHttp,甚至是原生的HttpURLConnection 等等,这里以HttpClient为例。 1、封装工具类 简单封装的get请求 2、测试方法 3、结果

    2024年02月09日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包