本篇是我學習反射的一個應用小場景而做的學習筆記,主要是一個小的總結,並對各個步驟的記錄,以便將來回顧。
一、基礎框架-敏捷基礎版本
這里假定我們要開發一個記事本,選擇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:
這時加入了改變顏色的功能:
由此可知,利用反射和接口,我們可以自定義插件實現不同的擴展功能,讓我們的主軟件項目更為強大!