C#实现插件的模式

前言

    面对不断变化的需求,我们的软件需要不断升级。应对这样一种变化,我们难道需要天天改我们的代码?看看Eclipse的实现,看看FireFox,他们貌似主程序都是不会变的,而是加载了许多的插件。所谓插件,不就是添加的代码吗?可是为什么我们的C#程序无法做到这点,无法在编译成exe后能继续扩展?本文就向大家介绍一种C#实现插件的模式。

大体设计

    对于这样的扩展,我决定使用dll作为我的扩展包。然后让程序自动搜索指定目录下的dll文件,调用dll文件中的函数实现升级和扩展。

    大体的流程图如下:

clip_image001

详细设计

    不难发现,如果我们直接使用C#调用dll,即使我们找到了dll文件,也没法知道里面的函数叫什么名字,即使可以枚举出来,也没法智能的调用里面的函数,实现我们预期的功能扩展。于是我们犯难了,我们已经写好的程序哪能预料以后会调用哪些dll的哪些函数呢?

    其实这个并不复杂,我们可以利用接口的技术实现这样一种功能。所谓接口,就是一份协议,当大家编写dll时都遵守这样一个协议,那么我们写的dll就可以方便的被exe调用。

clip_image001[5]

    接口里面定义了所有exe可能用到的方法,并且通过文档的形式规范化这些方法的使用过程,和对我们程序将要照成的影响。接口可以说是我们抽象出来的一些概念,只要是实现了这些概念化的东西的dll都可以被我们的exe拿来使用。因为exe明确知道了dll中会被使用的函数的名称和意义。这样的设计需要我们事先将我们的接口定义的完善。完善的接口会使我们的程序变得更加具有扩展性。

具体实现

具体的代码我分成了三大块。调用的exe,扩展的dll和我们定义的协议dll。

首先看我写的协议dll:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ControlInterface
{
    public interface IMyControl
    {
        event ChangeEventHandle OnChange;
        string IName
        {
            get;
        }
    }
    public delegate void ChangeEventHandle(Control c, string msg);
}
    这里我们定义了一个控件的名称IName和控件的一个事件OnChange。这样我们以后扩展的所有控件只要实现了这样一个接口,我们就可以调用他们的IName活动他们的名字,然后给他们的OnChange事件添加具体的方法。

然后看看我写的扩展用的控件dll:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ControlInterface;
namespace ControlInterface
{
    public partial class D1 : UserControl,IMyControl
    {
        public string name="D1";
        public ChangeEventHandle ButtonClick;
        public D1()
        {
            InitializeComponent();
        }

        public string IName
        {
            get
            {
                return name;
            }
        }

        #region IMyControl 成员

        event ChangeEventHandle IMyControl.OnChange
        {
            add { ButtonClick += value; }
            remove { ButtonClick -= value; }
        }

        #endregion

        private void button1_Click(object sender, EventArgs e)
        {
            ButtonClick(this, "Yes");
        }
    }
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ControlInterface;

namespace ControlInterface
{
    public partial class D2 : UserControl,IMyControl
    {
        public string name="D2";
        public event ChangeEventHandle TxtChange;
        public D2()
        {
            InitializeComponent();
        }

        public string IName
        {
            get
            {
                return name;
            }
        }

        #region IMyControl 成员

        event ChangeEventHandle IMyControl.OnChange
        {
            add { TxtChange += value; }
            remove { TxtChange -= value; }
        }

        #endregion

        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            TxtChange(this, textBox1.Text);
        }
    }
}

这里实现了D1和D2两个控件。这两个控件将生成一个dll。我们可以称为控件库。

最后是我写的调用的exe:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using ControlInterface;

namespace DynamicControl
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Assembly assembly = Assembly.LoadFrom(@"ControlLib.dll");
            Type[] types = assembly.GetTypes();
            foreach (Type type in types)
            {
                BindingFlags bflags =BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ;
                if (type.GetInterface("IMyControl")!=null)
                {
                    Object obj = type.InvokeMember("", bflags | BindingFlags.CreateInstance, null, null, null);
                    System.Windows.Forms.Control control = (Control)obj;
                    IMyControl iControl =obj as IMyControl;
                    iControl.OnChange += new ChangeEventHandle(iControl_OnChange);
                    TabPage tp = new TabPage(iControl.IName);
                    tabControl1.TabPages.Add(tp);
                    tp.Controls.Add(control);
                }
            }
        }

        void iControl_OnChange(Control c, string msg)
        {
            MessageBox.Show(msg);
        }
    }
}

这里通过反射机制动态的加载了这两个控件库里面的控件,并且通过我们定义好的接口,方便的访问到了控件库里面的控件。实现了我们需要的控件扩展。

尾声

    这样一种C#的插件模式只是我的一种实现方式,不一定是最好,但是代表着一种探索吧!

posted @ 2011-03-20 01:55  pangliang  阅读(7235)  评论(2编辑  收藏  举报