在.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 = ; 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:

这时加入了改变颜色的功能:

由此可知,利用反射和接口,我们可以自定义插件实现不同的扩展功能,让我们的主软件项目更为强大!
在.NET中使用反射实现简易插件机制的更多相关文章
- 【转】.Net中通过反射技术的应用----插件程序的开发入门
转自:http://www.cnblogs.com/winloa/archive/2012/03/25/2416355.html .Net中通过反射技术的应用----插件程序的开发入门 再开始之前,先 ...
- Spring 中的反射与反射的原理
作者:DeppWang.原文地址 在造轮子:实现一个简易的 Spring IoC 容器一文中提到 Spring 在创建 Bean 实例和依赖注入时使用了反射,本文来具体分析一下 Spring 中的反射 ...
- 详解C#中的反射
反射(Reflection) 2008年01月02日 星期三 11:21 两个现实中的例子:1.B超:大家体检的时候大概都做过B超吧,B超可以透过肚皮探测到你内脏的生理情况.这是如何做到的呢?B超是B ...
- python中的反射
在绝大多数语言中,都有反射机制的存在.从作用上来讲,反射是为了增加程序的动态描述能力.通俗一些,就是可以让用户参与代码执行的决定权.在程序编写的时候,我们会写很多类,类中又有自己的函数,对象等等.这些 ...
- .net中的反射(转载)
原文地址:http://www.cnblogs.com/Stephenchao/p/4481995.html 两个现实中的例子:1.B超:大家体检的时候大概都做过B超吧,B超可以透过肚皮探测到你内脏的 ...
- [.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦
[.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦 本节导读:上篇文章简单介绍了.NET面向对象中一个重要的技术反射的基本应用,它可以让我们动态的调 ...
- Java中的反射和注解
前言 在Java中,反射机制和注解机制一直是一个很重要的概念,那么他们其中的原理是怎么样呢,我们不仅仅需要会使用,更要知其然而之所以然. 目录 反射机制 反射如何使用 注解定义 注解机制原理 注解如何 ...
- 分享20款移动开发中很有用的 jQuery 插件
今天,很显然每个网站都需要有一个移动优化的界面以提高移动用户的使用体验.在开发任何移动项目时,要尽可能保持每一种资源尺寸都尽可能的小,以给最终用户提供一个好的体验是非常重要的.在这篇文章中我们已经编制 ...
- .Net 中的反射(序章) - Part.1
引言 反射是.Net提供给我们的一件强力武器,尽管大多数情况下我们不常用到反射,尽管我们可能也不需要精通它,但对反射的使用作以初步了解在日后的开发中或许会有所帮助. 反射是一个庞大的话题,牵扯到的知识 ...
随机推荐
- ASP.NET Boilerplate终于发布v1.0了
(此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:ABP经过2年多的开发,终于发布第一个主要版本了,谨此提醒ABP的使用者. ASP.N ...
- Spring-boot 开发Web应用
动态修改Freemarker模版: 设置模版属性: spring.freemarker.cache=false 启动应用方式有两种: a. 运行main()函数启动应用:则修改完模版文件后,需要把 ...
- 常用命令[Linux]
Linux文件类型 -:普通文件(f) d:目录文件 b:块设备文件(block) c:字符设备文件(character) l:符号链接文件(symbolic link file) p:命名管道文件( ...
- Beginning Scala study note(9) Scala and Java Interoperability
1. Translating Java Classes to Scala Classes Example 1: # a class declaration in Java public class B ...
- Haxe是何物?
最近对haxe很感兴趣,用一种语言统一所有的语言和平台,野心很大. 详细的介绍园子里已有大神发过,地址在这里:http://www.cnblogs.com/xiaotie/archive/2012/0 ...
- 转:Delphi的类与继承(VB与delphi比较)
既然已经做出了com程序用delphi来开发的决定,那当然就要对delphi进行一些深入的了解.有人说delphi是一个用控件堆砌起来的工具,和vb没什么两样:也有人说dephi实际上是面向过程的,他 ...
- Codeforces Round #366 (Div. 2)
CF 复仇者联盟场... 水题 A - Hulk(绿巨人) 输出love hate... #include <bits/stdc++.h> typedef long long ll; co ...
- 利用DelegatingHandler实现Web Api 的Api key校验
客户端在请求Web Api时可以有以下两种方式提供API key 基于Querystring提供Api key http://localhost:57967/Api/Values?key=12345 ...
- CentOS 6.5 Python Image Library 配置
转自:http://www.cnblogs.com/way_testlife/archive/2011/04/17/2019013.html PIL 下载: http://www.pythonware ...
- 弱省互测#2 t2
题意 给两个树,大小分别为n和m,现在两棵树各选一些点(包括1),使得这棵树以1号点为根同构(同构就是每个点的孩子数目相同),求最大的同构树.(n, m<=500) 分析 我们从两棵树中各取出一 ...