C#反射实现插件式开发

  • C#反射实现插件式开发已关闭评论
  • 126 次浏览
  • A+
所属分类:.NET技术
摘要

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


前言

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

项目介绍

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

目标框架:.NET Framework 4.6.1

项目架构和窗体布局:

C#反射实现插件式开发

客户端程序:

  • 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,也就是我们建立存放自定义插件的文件夹。当然,如果不怕麻烦,每次生成后,手动复制到此文件夹也可以,直接复制到客户端程序的..binPlugIns文件夹下。

C#反射实现插件式开发

插件路径配置

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

C#反射实现插件式开发

插件基类配置

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

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

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

C#反射实现插件式开发

调用演示

CustomPlugInA

C#反射实现插件式开发

CustomPlugInB

C#反射实现插件式开发

CustomPlugInC

C#反射实现插件式开发

CustomPlugInD

C#反射实现插件式开发

插件开发优缺点

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

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

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