C#反射实现插件式开发的过程详解

 更新时间:2023年09月16日 08:55:07   作者:码农阿亮  
插件式架构,一种全新的、开放性的、高扩展性的架构体系,插件式架构设计好处很多,把扩展功能从框架中剥离出来,降低了框架的复杂度,让框架更容易实现,这篇文章主要介绍了C#反射实现插件式开发,需要的朋友可以参考下

前言

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

项目介绍

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

目标框架: .NET Framework 4.6.1

项目架构和窗体布局:

客户端程序:

  • 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 文件夹下。

插件路径配置

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

插件基类配置

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

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

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

调用演示CustomPlugInA

CustomPlugInB

CustomPlugInC

CustomPlugInD

插件开发优缺点

  • 把扩展功能从框架中剥离出来,降低了框架的复杂度,让框架更容易实现
  • 宿主中可以对各个模块解析,完成插件间、插件和主程序间的通信。
  • 插件开发的可扩展性,灵活性比较高,而且可以进行定制化开发。

缺点

  • 每一个插件被编译成了dll,各模块无法单独运行,必须依托于主程序。
  • 修改插件时,由于生成的是dll,无法快速直观的查看修改以及调试。
  • 每一个插件必须依赖于某一个规范。

到此这篇关于C#反射实现插件式开发的文章就介绍到这了,更多相关C#反射插件式开发内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C#中类与结构的区别实例分析

    C#中类与结构的区别实例分析

    这篇文章主要介绍了C#中类与结构的区别,类与结构是C#初学者比较轻易混淆的概念,本文加以实例说明,需要的朋友可以参考下
    2014-08-08
  • C# 实现颜色渐变窗体控件详细讲解

    C# 实现颜色渐变窗体控件详细讲解

    这篇文章主要介绍了C# 实现颜色渐变窗体控件详细讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • C#实现数字转换

    C#实现数字转换

    这篇文章主要为大家详细介绍了C#实现数字转换,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-04-04
  • Unity3D Shader实现贴图切换效果

    Unity3D Shader实现贴图切换效果

    这篇文章主要为大家详细介绍了Unity3D Shader实现贴图切换效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-03-03
  • C# 获取程序集版本、文件版本

    C# 获取程序集版本、文件版本

    C# 获取程序集版本、文件版本的实现代码。
    2009-09-09
  • 重温C# clr 笔记总结

    重温C# clr 笔记总结

    本篇文章是对以前学习C# clr做的一些笔记,现在拿出来和大家分享下,希望需要的朋友能参考一下
    2013-05-05
  • C#中计数排序算法的原理及实现

    C#中计数排序算法的原理及实现

    计数排序是一种线性时间复杂度的排序方法,主要通过统计元素出现的次数实现排序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-10-10
  • C# 实现基于ADO.NET框架的DBHelper工具类(简化数据库操作)

    C# 实现基于ADO.NET框架的DBHelper工具类(简化数据库操作)

    ADO.NET是.NET框架中用于与数据库交互的核心组件,提供了一套用于连接数据库、执行SQL查询、插入、更新和删除数据的类库,包括SqlConnection、SqlCommand、SqlDataReader等,本文介绍如何使用DBHelper类封装数据库操作,以提高代码的可维护性和复用性,感兴趣的朋友一起看看吧
    2024-08-08
  • 使用C#进行音频处理的完整指南(从播放到编辑)

    使用C#进行音频处理的完整指南(从播放到编辑)

    在现代应用程序中,音频处理已经成为不可或缺的一部分,无论是开发一个简单的音频播放器,还是构建一个复杂的音频编辑工具,C#都提供了丰富的工具和库来实现这些功能,通过本文,我们将深入探索如何在C#中进行音频播放、录制、编辑、格式转换以及音频分析
    2025-04-04
  • c#连接mysql数据库的方法

    c#连接mysql数据库的方法

    这篇文章主要介绍了c#连接mysql数据库的方法,需要的朋友可以参考下
    2014-04-04

最新评论