最近在寫一篇關於如何擴展 Visual Studio 編輯器的文章時,用到了 MEF,所以打算寫一篇文章提一下這個技術點。本篇文章並不打算詳細介紹 MEF,只是一個最簡單的入門,相信您在閱讀本篇文章後,能夠迅速開發出一個可擴展的應用程序。服務器
MEF(Managed Extensibility Framework),是微軟推出的一款用於搭建可擴展應用程序的框架,起初是獨立於 .Net 發佈的,後來集成到了 .Net 4.0 中。使用該框架能夠很是輕鬆地擴展一個已發佈的應用程序的功能,連 Visual Studio IDE 中的代碼編輯器窗口也採用了MEF的思想,所以大大方便了開發人員對編輯器的擴展。框架
MEF 可用在任何使用 .NET Framework 的地方。能夠在客戶端應用程序中使用 MEF(不管應用程序使用的是 Windows 窗體、WPF,仍是任何其餘技術),也能夠在使用 ASP.NET 的服務器應用程序中使用 MEF。編輯器
Importpost
導入,這裏建議做爲一個名詞來理解,即一個接受者,它能夠接受外來的東東。就比如是下圖中的盒子,它能夠接受其它積木。this
十三孔智力盒編碼
Exportspa
導出,一樣建議以一個名詞來理解,即一個第三方的產物。它就像上圖中不一樣顏色的積木,這些積木不屬於這個盒子,可是能被放入盒子中,來豐富盒子的功能。設計
積木3d
Contractcode
協議。要想使盒子能接受積木(好比,圓柱體只能放入圓形的接口中),那這些積木必須符合必定的形狀。而這些形狀就至關因而應用程序和第三方擴展之間的一個協議。
Compose
組合(動詞),即將多個符合協議要求的部件組合在一塊兒,構成一個功能豐富的應用程序。就比如是將不一樣形狀的積木,按照接口的形狀組合在一塊兒。
MEF 會動態查找用戶所指定的目錄,若是發現該目錄中的程序集知足協議要求,就會啓動自身的組合引擎,而後根據不一樣的協議約定把這些擴展導入到應用程序內部。
對幾個關鍵的概念清楚了以後,咱們就能夠開始實踐了。最終的效果是窗體上會動態加載某一目錄下的dll,並自動爲每一個新功能添加一個按鈕,當點擊按鈕就會執行新的功能。
最終效果
首先,定義一個協議。
這個和普通定義接口沒什麼兩樣。
1 public interface IPlugin 2 { 3 string Text { get; } 4 5 void Do(); 6 }
安裝接受者
有了協議以後,就須要給應用程序安一個接受者。讓這個應用程序能夠經過接受者來獲取第三方擴展。MEF 提供了[Import] 和 [ImportMany] 兩種 attribute。 區別就是 Import 只能接受符合協議的一個擴展,而 ImportMany 能夠接受多個,並把多個擴展放入集合中。
1 public partial class Form1 : Form 2 { 3 public Form1() 4 { 5 InitializeComponent(); 6 } 7 8 [ImportMany] 9 public IEnumerable<IPlugin> plugins; 10 11 private void Form1_Load(object sender, EventArgs e) 12 { 13 } 14 15 }
提供一個符合協議的產物
這個產物的生產過程其實就是實現接口的過程,惟一的區別是咱們要爲這個實現打上個標籤,從而告訴咱們的組合引擎這個東西是給接受者的。MEF 提供了 Export 來暗示這是一個能夠提供給接受者的產物。
1 [Export(typeof(IPlugin))] 2 public class MyPlugin:IPlugin 3 { 4 public string Text 5 { 6 get 7 { 8 return "This is a demo"; 9 } 10 } 11 12 public void Do() 13 { 14 MessageBox.Show(Text); 15 } 16 }
發動引擎
萬事俱備,就差發動了。前面說了引擎的主要做用就是把發現擴展,同時把這些擴展組合到應用程序中。
1 private CompositionContainer _container; 2 private void Init() 3 { 4 try 5 { 6 MyPlugin my = new MyPlugin(); 7 this._container.ComposeParts(this, my);//把擴展和實例組合在一塊兒 8 } 9 catch (CompositionException compositionException) 10 { 11 Console.WriteLine(compositionException.ToString()); 12 } 13 }
上面的代碼雖然實現了組合的功能,可是卻硬把產物給編碼進去了。要是每次開發了新的擴展,都得這樣修改應用程序代碼,那就徹底沒有使用MEF的必要了,並且也違反了開放封閉的設計原則。
把上面的代碼改一改。
1 private CompositionContainer _container; 2 private void Init() 3 { 4 //設置目錄,讓引擎能自動去發現新的擴展 5 var catalog = new AggregateCatalog(); 6 catalog.Catalogs.Add(new DirectoryCatalog("D:\\plugin\\")); 7 8 //建立一個容器,至關因而生產車間 9 _container = new CompositionContainer(catalog); 10 11 //調用車間的ComposeParts把各個部件組合到一塊兒 12 try 13 { 14 this._container.ComposeParts(this);//這裏只須要傳入當前應用程序實例就能夠了,其它部分會自動發現並組裝 15 } 16 catch (CompositionException compositionException) 17 { 18 Console.WriteLine(compositionException.ToString()); 19 } 20 }
上面的代碼會自動去發現擴展,而後加入到應用程序中來。你要作的只是把新擴展的程序集放入 D:\\plugin 目錄中就能夠了。是否是很方便呢?
Managed Extensibility Framework (MEF)
本文來源於 《如何用 MEF 擴展應用程序》