C#反射实现插件式开发

c#,反射,实现,插件,开发 · 浏览次数 : 1222

小编点评

**项目介绍** 该项目介绍了一种名为**插件式架构**的全新、开放性、高扩展性的架构体系。 **架构设计** 插件式架构设计将扩展功能从框架中剥离出来,降低框架的复杂度,让框架更容易实现。扩展功能与框架以一种很松的方式耦合,方便开发人员进行定制化开发。 **主要特点** * **模块化设计:**插件可以被独立开发、部署和管理。 * **扩展性:**插件可以轻松添加或删除,无需修改框架本身。 * **定制化:**开发人员可以根据自己的需求进行定制化开发。 * **可维护性:**插件可以被独立维护和更新。 **开发优缺点** **优点:** * 扩展功能时易于添加或删除。 * 开发人员可以进行定制化开发。 * 可维护性增强。 **缺点:** * 每个插件只能与一个主程序模块关联。 * 开发和部署插件可能需要额外的步骤。 * 每个插件必须依赖于一个规范。 **代码获取** 要获取代码,需要关注公众号,后台回复关键字:Plugin。

正文

前言

插件式架构,一种全新的、开放性的、高扩展性的架构体系。插件式架构设计好处很多,把扩展功能从框架中剥离出来,降低了框架的复杂度,让框架更容易实现。扩展功能与框架以一种很松的方式耦合,两者在保持接口不变的情况下,可以独立变化和发布。基于插件设计并不神秘,相反它比起一团泥的设计更简单,更容易理解。

项目介绍

书写4个插件类库,分别传参实现“加减乘除”运算,调用插件的客户端采用Winform窗体程序。

目标框架:.NET Framework 4.6.1

项目架构和窗体布局:

image

客户端程序:

  • PluginApp:反射调用插件

插件描述:

  • PluginBase:规范插件的基类,定义抽象类,开发的插件的类需要继承此类,代表遵守这个规范。
  • CustomPlugInA:实现加法的插件
  • CustomPlugInB:实现减法的插件
  • CustomPlugInC:实现乘法的插件
  • CustomPlugInD:实现除法的插件

代码实现

插件基类
 /// <summary>
    ///插件基类
    /// </summary>
    public abstract class Base
    {
        /// <summary>
        /// 插件名称
        /// </summary>
        /// <returns></returns>
        public abstract string Name();
       /// <summary>
       /// 插件描述
       /// </summary>
       /// <returns></returns>
        public abstract string Desc();
        /// <summary>
        /// 执行方法
        /// </summary>
        /// <param name="param1">参数1</param>
        /// <param name="param2">参数2</param>
        /// <returns></returns>
        public abstract string Run(int param1, int param2);
        /// <summary>
        /// 版本 
        /// </summary>
        public string Version
        {
            get { return "1.0.0"; }
        }
    }
PlugInA
    public class PlugInA: Base
    {

        public override string Name()
        {
            return "PlugInA";
        }

        public override string Desc()
        {
            return "加法";
        }

        public override string Run(int param1,int param2)
        {
            return (param1 + param2) + "";
        }
    }
}
PlugInB
    public class PlugInB : Base
    {

        public override string Name()
        {
            return "PlugInB";
        }

        public override string Desc()
        {
            return "减法";
        }

        public override string Run(int param1, int param2)
        {
            return (param1 - param2) + "";
        }
    }
PlugInC
  public class PlugInC : Base
    {

        public override string Name()
        {
            return "PlugInC";
        }

        public override string Desc()
        {
            return "乘法";
        }

        public override string Run(int param1, int param2)
        {
            return (param1 * param2) + "";
        }
    }
PlugInD
 public class PlugInD : Base
    {

        public override string Name()
        {
            return "PlugInD";
        }

        public override string Desc()
        {
            return "除法";
        }

        public override string Run(int param1, int param2)
        {
            return (param1 / param2) + "";
        }
    }
客户端核心代码:
   public partial class FrmMain : Form
    {
        public FrmMain()
        {
            InitializeComponent();
            dgrvPlugins.AutoGenerateColumns = false;
        }

        List<PluginModel> List = new List<PluginModel>();

        readonly string PlugInPath = Application.StartupPath + "\\PlugIns";

        /// <summary>
        /// 载入插件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btLoadPlugins_Click(object sender, EventArgs e)
        {

            if (!Directory.Exists(PlugInPath))
            {
                Directory.CreateDirectory(PlugInPath);
            }
            List.Clear();
            string[] files = Directory.GetFiles(PlugInPath);
            foreach (string file in files)
            {
                if (file.ToLower().EndsWith(".dll"))
                {
                    try
                    {
                        Assembly assembly = Assembly.LoadFrom(file);
                        Type[] types = assembly.GetTypes();
                        foreach (Type type in types)
                        {
                            if (type.BaseType.FullName == "PlugInBase.Base")
                            {
                                object obj = assembly.CreateInstance(type.FullName);
                                string name = type.GetMethod("Name").Invoke(obj, null).ToString();
                                string desc = type.GetMethod("Desc").Invoke(obj, null).ToString();
                                string version = type.GetProperty("Version").GetValue(obj).ToString();

                                List.Add(new PluginModel
                                {
                                    Name = name,
                                    Desc = desc,
                                    Version = version,
                                    type = type,
                                    Obj = obj
                                });
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                }
            }

            dgrvPlugins.DataSource = new BindingList<PluginModel>(List);
        }

        /// <summary>
        /// 打开插件目录
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btOpenPluginDir_Click(object sender, EventArgs e)
        {
            Process.Start(PlugInPath);
        }


        /// <summary>
        /// 执行选中插件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btExcute_Click(object sender, EventArgs e)
        {
            //获取选择的插件信息
            int index = dgrvPlugins.CurrentRow.Index;
            object obj = List[index].Obj;
            Type type = List[index].type;
            //参数
            object[] inParams = new object[2];
            inParams[0] =Convert.ToInt32( dgrvPlugins.CurrentRow.Cells[2].Value);
            inParams[1] = Convert.ToInt32(dgrvPlugins.CurrentRow.Cells[3].Value);
            object value = type.GetMethod("Run").Invoke(obj, inParams);
            MessageBox.Show(Convert.ToString(value),"结果",MessageBoxButtons.OK);
        }
    }

项目配置

插件生成配置

编译生成项目的时候需要注意,此处的调用插件是通过反射调用.dll中类和方法,所以首先要找到这个.dll的文件,所以此处我们在Winform客户端程序下建立一个存放类库dll的文件PlugIns,在插件类库项目生成后事件命令中,填入如下命令:

copy /Y "$(TargetDir)$(ProjectName).dll" "$(SolutionDir)\PlugIns"

以上命令代表,在项目的类库生成后,将类库copy到解决方案的路径子文件夹PlugIns,也就是我们建立存放自定义插件的文件夹。当然,如果不怕麻烦,每次生成后,手动复制到此文件夹也可以,直接复制到客户端程序的..\bin\PlugIns文件夹下。

image

插件路径配置

全选这些类库,把这些类库设置为"如果较新则复制",这样每次在编译客户端程序,如果自定义插件有更新,则同步会复制到bin目录下

image

插件基类配置

插件基类提供了规范,需要在类库的生成后事件,添加命令:

copy /Y "$(TargetDir)$(ProjectName).dll" "$(SolutionDir)\bin\Debug"

将生成的dll文件,拷贝到客户端程序的bin路径下

image

调用演示

CustomPlugInA

image

CustomPlugInB

image

CustomPlugInC

image

CustomPlugInD

image

插件开发优缺点

优点
  • 把扩展功能从框架中剥离出来,降低了框架的复杂度,让框架更容易实现
  • 宿主中可以对各个模块解析,完成插件间、插件和主程序间的通信。
  • 插件开发的可扩展性,灵活性比较高,而且可以进行定制化开发。
缺点
  • 每一个插件被编译成了dll,各模块无法单独运行,必须依托于主程序。

  • 修改插件时,由于生成的是dll,无法快速直观的查看修改以及调试。

  • 每一个插件必须依赖于某一个规范。

源码获取

关注公众号,后台回复关键字:Plugin

与C#反射实现插件式开发相似的内容:

C#反射实现插件式开发

前言 插件式架构,一种全新的、开放性的、高扩展性的架构体系。插件式架构设计好处很多,把扩展功能从框架中剥离出来,降低了框架的复杂度,让框架更容易实现。扩展功能与框架以一种很松的方式耦合,两者在保持接口不变的情况下,可以独立变化和发布。基于插件设计并不神秘,相反它比起一团泥的设计更简单,更容易理解。

.NET Core反射获取带有自定义特性的类,通过依赖注入根据Attribute元数据信息调用对应的方法

前言 前段时间有朋友问道一个这样的问题,.NET Core中如何通过Attribute的元数据信息来调用标记的对应方法。我第一时间想到的就是通过C#反射获取带有Custom Attribute标记的类,然后通过依赖注入(DI)的方式获取对应服务的方法并通过反射动态执行类的方法,从而实现更灵活的编程方

【c#版本Openfeign】Net8 自带OpenFeign实现远程接口调用

引言 相信巨硬,我们便一直硬。Net版本到现在已经出了7了,8也已经在预览版了,相信在一个半月就会正式发布,其中也有很多拭目以待的新功能了,不仅仅有Apm和Tap的结合,TaskToAscynResult,以及UnsafeAccessor用来获取私有变量,性能比反射,EMIT更高,还有针对Async

4.1 C++ STL 动态链表容器

List和SList都是C++ STL中的容器,都是基于双向链表实现的,可以存储可重复元素的特点。其中,List内部的节点结构包含两个指针一个指向前一个节点,一个指向后一个节点,而SList只有一个指针指向后一个节点,因此相对来说更节省存储空间,但不支持反向遍历,同时也没有List的排序功能。双向链表的数据元素可以通过链表指针串接成逻辑意义上的线性表,不同于采用线性表顺序存储结构的`Vector`

C#判断字符串是否是有效的XML格式数据

说明 在try-catch语句块中,创建XmlDocument对象,并使用LoadXml方法加载xml字符串。如果没有异常,则说明xml字符串是有效的,返回true,反之为false。 代码实现 /// /// Xml字符串格式验证 /// ///

驱动开发:内核文件读写系列函数

在应用层下的文件操作只需要调用微软应用层下的`API`函数及`C库`标准函数即可,而如果在内核中读写文件则应用层的API显然是无法被使用的,内核层需要使用内核专有API,某些应用层下的API只需要增加Zw开头即可在内核中使用,例如本章要讲解的文件与目录操作相关函数,多数ARK反内核工具都具有对文件的管理功能,实现对文件或目录的基本操作功能也是非常有必要的。

Linux/Golang/glibC系统调用

Linux/Golang/glibC系统调用 本文主要通过分析Linux环境下Golang的系统调用,以此阐明整个流程 有时候涉略过多,反而遭到质疑~,写点文章证明自己实力也好 Golang系统调用 找个函数来分析 https://pkg.go.dev/os/exec#Cmd.Wait 源码文件在s

C#反射

目录C#反射概述语法应用场景总结引用 C#反射 概述 C# 反射(Reflection)是一种强大的机制,它允许程序在运行时访问和操作 .NET 程序集中的类型和成员。 获取程序集、模块 和类型成员信息,三者关系介绍请查看。 语法 反射的核心概念是 Type 对象。Type 对象表示一个 .NET

聊一聊对一个 C# 商业程序的反反调试

一:背景 1.讲故事 前段时间有位朋友在微信上找到我,说他对一个商业的 C# 程序用 WinDbg 附加不上去,每次附加之后那个 C# 程序就自动退出了,问一下到底是怎么回事?是不是哪里搞错了,有经验的朋友应该知道,其实这是 商业程序 的反调试机制捣鬼的,为了保护程序隐私,一般都不希望他人对自己做逆

都说 C++ 没有 GC,RAII: 那么我算个啥?

学过 Java、C# 或者其他托管语言(managed languages)的同学,回过头来看 C++ 的时候,第一反应就是 C++ 没有自动垃圾回收器(GC),而不能充分利用的资源被称为垃圾。