在.NET中使用反射实现简易插件机制
本篇是我学习反射的一个应用小场景而做的学习笔记,主要是一个小的总结,并对各个步骤的记录,以便将来回顾。
一、基础框架-敏捷基础版本
这里假定我们要开发一个记事本,选择Windows Form技术开发,界面如下图所示:
该记事本只提供了一个TextBox供输入,以及保存到指定文件。其他功能均没有实现,假定我们先把这个版本做出来,后续功能通过插件形式一步一步完成。
但是,为了能够使用插件,我们的主项目还得经过一些改造:
(1)加载时需要从插件目录中获取插件
public FormMain() { InitializeComponent(); // 加载插件 LoadPlugins(); } private void LoadPlugins() { // 1.加载plugins目录下的所有的dll文件 string plugins = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "plugins"); // 1.1 搜索plugins目录下的所有的dll文件 string[] dlls = Directory.GetFiles(plugins, "*.dll"); // 2.循环将每个dll文件都加载起来 foreach (string dllPath in dlls) { // 2.1 动态加载当前循环的dll文件 Assembly assembly = Assembly.LoadFile(dllPath); // 2.2 获取当前dll中的所有的public类型 Type[] types = assembly.GetExportedTypes(); // 2.3 获取IEditor接口的Type Type typeIEditor = typeof(IEditor); for (int i = 0; i < types.Length; i++) { // 2.4 验证当前的类型即实现了IEditor接口并且该类型还可以被实例化 if (typeIEditor.IsAssignableFrom(types[i]) && !types[i].IsAbstract) { IEditor editor = (IEditor)Activator.CreateInstance(types[i]); // 2.5 向菜单栏中动态添加一个菜单项 ToolStripItem toolItem = toolstripEditMenu.DropDownItems.Add(editor.PluginName); // 2.6 为刚刚增加的菜单项注册一个单击事件 toolItem.Click += new EventHandler(toolItem_Click); toolItem.Tag = editor; } } } }
(2)为插件设置通用的Click事件
private void toolItem_Click(object sender, EventArgs e) { ToolStripItem item = sender as ToolStripItem; if (item != null) { if (item.Tag != null) { IEditor editor = item.Tag as IEditor; if (editor != null) { // 运行该插件 editor.Execute(this.txtContent); } } }
这里约定所有插件都实现了IEditor接口,并且所有插件的功能都在Execute方法中被实现。
二、约定接口-可扩展的基础
不难发现,如果我们直接使用反射调用dll,即使我们找到了dll文件,也没法知道里面的函数叫什么名字,即使可以枚举出来,也没法智能的调用里面的函数,实现我们预期的功能扩展。于是我们犯难了,我们已经写好的程序哪能预料以后会调用哪些dll的哪些函数呢?
其实这个并不复杂,我们可以利用接口技术实现这样一种功能。所谓接口,就是一份协议,当大家编写dll时都遵守这样一个协议,那么我们写的dll就可以方便的被exe调用。
对于这个小demo而言,我们设计一个IEditor接口如下:
public interface IEditor { string PluginName { get; } void Execute(TextBox txtbox); }
其中,PluginName是插件的名称,用于菜单显示。Execute方法则接收记事本的TextBox控件,用于实现具体的功能。
三、实现插件-可升级的功能
(1)插件1:将文本全部转为大写
新建一个类库项目,设计一个实现IEditor接口的类:
public class ChangeFontStyle : IEditor { public string PluginName { get { return "转为大写"; } } public void Execute(TextBox txtbox) { if (!string.IsNullOrEmpty(txtbox.Text)) { txtbox.Text = txtbox.Text.ToUpper(); } else { MessageBox.Show("请先输入文字!"); } }
(2)插件2:将文本全部变为红色
新建一个类库项目,设计一个实现IEditor接口的类:
public class ChangeFontColor : IEditor { public string PluginName { get { return "改变颜色"; } } public void Execute(TextBox txtbox) { if (!string.IsNullOrEmpty(txtbox.Text)) { txtbox.ForeColor = System.Drawing.Color.Red; } else { MessageBox.Show("请先输入文字!"); } } }
四、拥抱变化-简单的测试
(1)没有任何插件的记事本程序
Plugins 插件目录下一个dll也木有:
因此我们的记事本只有最基本的操作:
(2)加入插件1(转换大写)的记事本程序
Plugins 插件目录有一个dll:
这时加入了转换大写的功能:
(3)加入插件2(改变颜色)的记事本程序
Plugins 插件目录有两个dll:
这时加入了改变颜色的功能:
由此可知,利用反射和接口,我们可以自定义插件实现不同的扩展功能,让我们的主软件项目更为强大!
相关文章
- 老杜带你学Ajax,轻松掌握ajax底层实现原理
- .NET 使用 Coverlet 统计单元测试覆盖率
- 使用 coverlet 查看.NET Core应用的测试覆盖率
- ASP.NET Core - 配置系统之配置添加
- [C#]使用Costura.Fody将源DLL合并到目标EXE
- Fireasy3 揭秘 -- 使用 SourceGeneraor 实现动态代理(AOP)
- EF7DbContext池
- MASA Framework源码解读-01 MASAFactory工厂设计(一个接口多个实现的最佳姿势)
- IO流思维导图
- WPF学习笔记03-布局Layout
- o2s.components.pdfView4net.dll 下个别pdf扫描件不显示问题,已经解决单不知道原因。。期望解答
- WPF学习笔记02-XAML语法
- Avalonia 实现平滑拖动指定控件
- c# 使用遇到的问题记录
- C#/.NET/.NET Core Dapper 批量删除、新增、修改说明
- RestSharp —(个人胡说八道🤣)
- Winform 程序多开
- C# 针对特定的条件进行锁操作,不用lock,而是mutex
- C# 获取电脑内存、CPU信息
- C#获取CPU和内存使用率