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

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

  由此可知,利用反射和接口,我们可以自定义插件实现不同的扩展功能,让我们的主软件项目更为强大!

作者:周旭龙

出处:http://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

时间: 2024-10-11 00:33:27

在.NET中使用反射实现简易插件机制的相关文章

行为扩展以及插件机制

在thinkPHP中的行为扩展和插件机制. 首先行为扩展这个概念是TP框架的核心组成之一,关于行为的解释我就粗略的概括一下吧: TP在从接受到HTTP请求到最终将试图输出,期间经历的很多步骤,这些步骤大家可以在http://document.thinkphp.cn/manual_3_2.html#system_process这里面看到. 那么行为扩展实际上就是在这些流程里面买下了一个钩子,你可以往钩子里添加你自己的业务逻辑 当程序执行到某些钩子位置时将自动触发你的业务逻辑,http://docu

PHP中的反射

面向对象编辑中对象被赋予了自省的能力,而这个自省的过程就是反射. 反射,直观理解应时根据到达地找出出发地和来源.比方说,我给你一个光秃秃的对象,我可以仅仅通过这个对象就能知道它所属的类,拥有哪些方法. 反射指在PHP运行状态中,扩展分析PHP程序,导出或提取出关于类,方法,属性,参数等详细信息,包括注释.这种动态获取信息以及动态调用对象方法的功能称为反射API 如何使用反射API 以下面代码为例 class HandsonBoy { public $name = 'chenqionghe'; p

php中的钩子(hook插件机制)

对"钩子"这个概念其实不熟悉,最近看到一个php框架中用到这种机制来扩展项目,所以大概来了解下. hook插件机制的基本思想: 在项目代码中,你认为要扩展(暂时不扩展)的地方放置一个钩子函数,等需要扩展的时候,把需要实现的类和函数挂载到这个钩子上,就可以实现扩展了. 思想就是这样听起来比较笼统,看一个网上的实现的例子. 整个插件机制包含三个部分: 1.hook插件经理类:这个是核心文件,是一个应用程序全局Global对象.它主要有三个职责 1>监听已经注册了的所有插件,并实例化这

【转】在Eclipse中安装和使用TFS插件

文章地址:http://www.cnblogs.com/judastree/archive/2012/09/05/2672640.html 问题: 在Eclipse中安装和使用TFS插件. 解决过程: 在Eclipse中安装插件的方法其实都一样,安装TFS的步骤如下: 下载TFS插件.你可以到微软的下载中心,下载TFS插件TFSEclipsePlugin-UpdateSiteArchive-10.0.0.zip. 下载完毕之后,打开Eclipse. 点击Help菜单中的Install New S

在Gradle中使用jaxb的xjc插件

jaxb,全称为Java Architecture for Xml Binding,是一种将java对象与xml建立起映射的技术.其主要提供两个功能,一是将java对象映射为xml,二是将xml映射为java对象.JAXB有1.0版和2.0版.2.0版对应的JSR(Java specification request, java规格要求)是JSR 222.jaxb中的xjc工具能够将XML Schema转换为对应的java类.支持的XML类型包括XML DTD,XSD以及WSDL.而schema

Android4.2.2下Stagefright多媒体架构中的A31的OMX插件和Codec组件

本文均属自己阅读源码的点滴总结,转账请注明出处谢谢. 欢迎和大家交流.qq:1037701636 email: [email protected] 在前面的博文中提到,AwesomePlayer::onPrepareAsyncEvent()开始进行Codec解码器组件的获取以及创建,这里和大家分享. 1.以解码器实例作为切入点 status_t AwesomePlayer::initVideoDecoder(uint32_t flags) { ATRACE_CALL(); ...... ALOG

MyEclipse 10 中安装Android ADT 22插件的方法

MyEclipse 10 中安装Android ADT 22插件的方法 下载ADT包:http://dl.google.com/android/ADT-22.0.0.zip 将ADT-22.0.0.zip文件放在指定的目录下,例如D:\Programs\Android,不解压. 打开MyEclipse,点击菜单Help >MyEclipse Configuration Center,如下图 在MyEclipse Configuration Center中,点击Software标签,再点击add

java中利用反射机制绕开编译器对泛型的类型限制

首先看下面这个例子 public static void main(String[] args) { ArrayList<Integer> al1 = new ArrayList<Integer>(); al1.add(1); ArrayList<String> al2 = new ArrayList<String>(); al2.add("hello"); //int型链表和string型链表,结果为true System.out.pr

MyEclipse 10.0破解,及建立Myeclipse中建立JFrame 和Swing插件的使用

一.MyEclipse 注册码生成 免积分下载 http://download.csdn.net/detail/u014112584/7270453 具体使用过程: myeclipse 9.1.10 破解 激活,java编写,适用于装有java环境的各种操作系统,win,linux,maxos 第一步:输入任意用户名 第二步:点击Systemid... 按钮,自动生成本机器的systemid. 第三步: 点菜单Tools->RebuildKey 第四步:点击active按钮.会在显示区域生成 L