C#反射实现插件式开发
<h2 id="前言">前言</h2><p>插件式架构,一种全新的、开放性的、高扩展性的架构体系。插件式架构设计好处很多,把扩展功能从框架中剥离出来,降低了框架的复杂度,让框架更容易实现。扩展功能与框架以一种很松的方式耦合,两者在保持接口不变的情况下,可以独立变化和发布。基于插件设计并不神秘,相反它比起一团泥的设计更简单,更容易理解。</p>
<h2 id="项目介绍">项目介绍</h2>
<p>书写4个插件类库,分别传参实现“加减乘除”运算,调用插件的客户端采用Winform窗体程序。</p>
<p><strong>目标框架:</strong><code>.NET Framework 4.6.1</code></p>
<p><strong>项目架构和窗体布局:</strong></p>
<p><img src="https://img2023.cnblogs.com/blog/1913282/202309/1913282-20230915234302118-1459120257.png" alt="image" loading="lazy"></p>
<p><strong>客户端程序:</strong></p>
<ul>
<li>PluginApp:反射调用插件</li>
</ul>
<p><strong>插件描述:</strong></p>
<ul>
<li>PluginBase:规范插件的基类,定义抽象类,开发的插件的类需要继承此类,代表遵守这个规范。</li>
<li>CustomPlugInA:实现加法的插件</li>
<li>CustomPlugInB:实现减法的插件</li>
<li>CustomPlugInC:实现乘法的插件</li>
<li>CustomPlugInD:实现除法的插件</li>
</ul>
<h2 id="代码实现">代码实现</h2>
<h5 id="插件基类">插件基类</h5>
<pre><code class="language-C#"> /// <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"; }
}
}
</code></pre>
<h5 id="plugina">PlugInA</h5>
<pre><code class="language-C#"> 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) + "";
}
}
}
</code></pre>
<h5 id="pluginb">PlugInB</h5>
<pre><code class="language-C#"> 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) + "";
}
}
</code></pre>
<h5 id="pluginc">PlugInC</h5>
<pre><code class="language-C#">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) + "";
}
}
</code></pre>
<h5 id="plugind">PlugInD</h5>
<pre><code class="language-C#"> 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) + "";
}
}
</code></pre>
<h5 id="客户端核心代码">客户端核心代码:</h5>
<pre><code class="language-C#"> 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.Obj;
Type type = List.type;
//参数
object[] inParams = new object;
inParams =Convert.ToInt32( dgrvPlugins.CurrentRow.Cells.Value);
inParams = Convert.ToInt32(dgrvPlugins.CurrentRow.Cells.Value);
object value = type.GetMethod("Run").Invoke(obj, inParams);
MessageBox.Show(Convert.ToString(value),"结果",MessageBoxButtons.OK);
}
}
</code></pre>
<h2 id="项目配置">项目配置</h2>
<h5 id="插件生成配置">插件生成配置</h5>
<p>编译生成项目的时候需要注意,此处的调用插件是通过反射调用.dll中类和方法,所以首先要找到这个.dll的文件,所以此处我们在Winform客户端程序下建立一个存放类库dll的文件<code>PlugIns</code>,在插件类库项目生成后事件命令中,填入如下命令:</p>
<pre><code class="language-C#">copy /Y "$(TargetDir)$(ProjectName).dll" "$(SolutionDir)\PlugIns"
</code></pre>
<p>以上命令代表,在项目的类库生成后,将类库copy到解决方案的路径子文件夹<code>PlugIns</code>,也就是我们建立存放自定义插件的文件夹。当然,如果不怕麻烦,每次生成后,手动复制到此文件夹也可以,直接复制到客户端程序的<code>..\bin\PlugIns</code>文件夹下。</p>
<p><img src="https://img2023.cnblogs.com/blog/1913282/202309/1913282-20230915234342103-1149886871.png" alt="image" loading="lazy"></p>
<h5 id="插件路径配置">插件路径配置</h5>
<p>全选这些类库,把这些类库设置为"如果较新则复制",这样每次在编译客户端程序,如果自定义插件有更新,则同步会复制到<code>bin</code>目录下</p>
<p><img src="https://img2023.cnblogs.com/blog/1913282/202309/1913282-20230915234357876-2087180777.png" alt="image" loading="lazy"></p>
<h5 id="插件基类配置">插件基类配置</h5>
<p>插件基类提供了规范,需要在类库的生成后事件,添加命令:</p>
<pre><code class="language-C#">copy /Y "$(TargetDir)$(ProjectName).dll" "$(SolutionDir)\bin\Debug"
</code></pre>
<p>将生成的dll文件,拷贝到客户端程序的<code>bin</code>路径下</p>
<p><img src="https://img2023.cnblogs.com/blog/1913282/202309/1913282-20230915234428251-1488127776.png" alt="image" loading="lazy"></p>
<h2 id="调用演示">调用演示</h2>
<h5 id="customplugina">CustomPlugInA</h5>
<p><img src="https://img2023.cnblogs.com/blog/1913282/202309/1913282-20230915234618788-1538253365.gif" alt="image" loading="lazy"></p>
<h5 id="custompluginb">CustomPlugInB</h5>
<p><img src="https://img2023.cnblogs.com/blog/1913282/202309/1913282-20230915234642414-150365001.gif" alt="image" loading="lazy"></p>
<h5 id="custompluginc">CustomPlugInC</h5>
<p><img src="https://img2023.cnblogs.com/blog/1913282/202309/1913282-20230915234655084-1371632081.gif" alt="image" loading="lazy"></p>
<h5 id="customplugind">CustomPlugInD</h5>
<p><img src="https://img2023.cnblogs.com/blog/1913282/202309/1913282-20230915234726366-1813289233.gif" alt="image" loading="lazy"></p>
<h2 id="插件开发优缺点">插件开发优缺点</h2>
<h5 id="优点">优点</h5>
<ul>
<li>把扩展功能从框架中剥离出来,降低了框架的复杂度,让框架更容易实现</li>
<li>宿主中可以对各个模块解析,完成插件间、插件和主程序间的通信。</li>
<li>插件开发的可扩展性,灵活性比较高,而且可以进行定制化开发。</li>
</ul>
<h5 id="缺点">缺点</h5>
<ul>
<li>
<p>每一个插件被编译成了dll,各模块无法单独运行,必须依托于主程序。</p>
</li>
<li>
<p>修改插件时,由于生成的是dll,无法快速直观的查看修改以及调试。</p>
</li>
<li>
<p>每一个插件必须依赖于某一个规范。</p>
</li>
</ul>
<h2 id="源码获取">源码获取</h2>
<p><strong>关注公众号,后台回复关键字:<code>Plugin</code></strong></p>
</div>
<div id="MySignature" role="contentinfo">
<p>本文来自博客园,作者:码农阿亮,转载请注明原文链接:https://www.cnblogs.com/wml-it/p/17706182.html</p>
<hr/>
<font size="2" color="black"><b>技术的发展日新月异,随着时间推移,无法保证本博客所有内容的正确性。如有误导,请大家见谅,欢迎评论区指正!</b></font>
<br/>
<font size="2" color="green"><b>开源库地址,欢迎点亮:</b></font>
<br/>
<font size="2" color="red"><b>GitHub:</b></font>https://github.com/ITMingliang
<br/>
<font size="2" color="red"><b>Gitee:</b></font> https://gitee.com/mingliang_it
<br/>
<font size="2" color="red"><b>GitLab:</b></font> https://gitlab.com/ITMingliang
<br/>
<hr/>
<font size="2" color="red"><b>建群声明:</b></font>
<font size="2" color="#6600cc"><b>本着技术在于分享,方便大家交流学习的初心,特此建立【编程内功修炼交流群】,为大家答疑解惑。热烈欢迎各位爱交流学习的程序员进群,也希望进群的大佬能不吝分享自己遇到的技术问题和学习心得!进群方式:扫码关注公众号,后台回复【进群】。</b></font>
<br/>
<!-- <font size="2" color="green"><b>【微信公众号】:</b> </font> -->
<br/>
<!-- <img src="https://images.cnblogs.com/cnblogs_com/wml-it/2196906/t_220731064016_wx.jpg"width="180px" height="180px"/>-->
<!-- <img src="https://images.cnblogs.com/cnblogs_com/wml-it/2196906/o_230807062355_gongzhonghao.jpg"width="180px" height="180px"/> -->
<img src="https://images.cnblogs.com/cnblogs_com/wml-it/2196906/o_230823011905_wxgzhss.png"width="880px" height="280px" />
<hr/><br><br>
来源:https://www.cnblogs.com/wml-it/p/17706182.html
頁:
[1]