在.NET中使用反射實現簡易插件機制


  本篇是我學習反射的一個應用小場景而做的學習筆記,主要是一個小的總結,並對各個步驟的記錄,以便將來回顧。

一、基礎框架-敏捷基礎版本

  這里假定我們要開發一個記事本,選擇Windows Form技術開發,界面如下圖所示:

Main

  該記事本只提供了一個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方法中被實現。

二、約定接口-可擴展的基礎

plugin

  不難發現,如果我們直接使用反射調用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也木有:

NoPlugins

    因此我們的記事本只有最基本的操作: 

demo1

  (2)加入插件1(轉換大寫)的記事本程序

    Plugins 插件目錄有一個dll:

oneplugin

    這時加入了轉換大寫的功能:

demo2

  (3)加入插件2(改變顏色)的記事本程序

     Plugins 插件目錄有兩個dll:

twopluings

     這時加入了改變顏色的功能:

demo3

  由此可知,利用反射和接口,我們可以自定義插件實現不同的擴展功能,讓我們的主軟件項目更為強大!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM