在.NET中使用反射实现简易插件机制

  本篇是我学习反射的一个应用小场景而作的学习笔记,主要是一个小的总结,并对各个步骤的记录,以便未来回顾。框架

1、基础框架-敏捷基础版本

  这里假定咱们要开发一个记事本,选择Windows Form技术开发,界面以下图所示:函数

Main

  该记事本只提供了一个TextBox供输入,以及保存到指定文件。其余功能均没有实现,假定咱们先把这个版本作出来,后续功能经过插件形式一步一步完成。学习

  可是,为了可以使用插件,咱们的主项目还得通过一些改造:测试

  (1)加载时须要从插件目录中获取插件ui

    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事件this

    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方法中被实现。spa

2、约定接口-可扩展的基础

plugin

  不难发现,若是咱们直接使用反射调用dll,即便咱们找到了dll文件,也无法知道里面的函数叫什么名字,即便能够枚举出来,也无法智能的调用里面的函数,实现咱们预期的功能扩展。因而咱们犯难了,咱们已经写好的程序哪能预料之后会调用哪些dll的哪些函数呢?插件

  其实这个并不复杂,咱们能够利用接口技术实现这样一种功能。所谓接口,就是一份协议,当你们编写dll时都遵照这样一个协议,那么咱们写的dll就能够方便的被exe调用。设计

  对于这个小demo而言,咱们设计一个IEditor接口以下:3d

    public interface IEditor
    {
        string PluginName
        {
            get;
        }

        void Execute(TextBox txtbox);
    }

  其中,PluginName是插件的名称,用于菜单显示。Execute方法则接收记事本的TextBox控件,用于实现具体的功能。

3、实现插件-可升级的功能

  (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("请先输入文字!");
            }
        }
    }

4、拥抱变化-简单的测试

  (1)没有任何插件的记事本程序

    Plugins 插件目录下一个dll也木有:

NoPlugins

    所以咱们的记事本只有最基本的操做: 

demo1

  (2)加入插件1(转换大写)的记事本程序

    Plugins 插件目录有一个dll:

oneplugin

    这时加入了转换大写的功能:

demo2

  (3)加入插件2(改变颜色)的记事本程序

     Plugins 插件目录有两个dll:

twopluings

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

demo3

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

 

相关文章
相关标签/搜索