概述
Managed Extensibility Framework(MEF)是.NET平臺下的一個擴展性管理框架,它是一系列特性的集合,包括依賴注入(DI)以及Duck Typing等。MEF爲開發人員提供了一個工具,讓咱們能夠輕鬆的對應用程序進行擴展而且對已有的代碼產生最小的影響,開發人員在開發過程當中根據功能要求定義一些擴展點,以後擴展人員就可使用這些擴展點與應用程序交互;同時MEF讓應用程序與擴展程序之間不產生直接的依賴,這樣也容許在多個具備一樣的擴展需求之間共享擴展程序。html
本文將介紹一下Managed Extensibility Framework的一些簡單使用。框架
簡單依賴注入
你們能夠去這裏http://code.msdn.microsoft.com/mef下載MEF的CTP版本,在下載包裏有一些簡單的文檔和示例。下面先來看一個簡單的示例,這裏輸出一個字符串:ide
// Author: TerryLee 2008.8.30 // URL: http://www.cnblogs.com/Terrylee static void Main(string[] args) { Console.WriteLine("This is a simple string."); }
如今考慮到該字符串未來可能發生變化,不知道該字符串將從何處取得,也就是說這裏有多是一個變化點,也是一個擴展點,那咱們如今使用MEF對其進行從新設計,咱們將會把這個過程分紅兩個部分,一部分用於提供字符串,而另外一部分則用來使用字符串,定義一個字符串提供程序,你們注意到這裏爲OutputTitle屬性添加了一個Export特性,這標識着此處爲一個輸出,它使的MEF可以對其進行識別,以下代碼所示:函數
// Author: TerryLee 2008.8.30 // URL: http://www.cnblogs.com/Terrylee public class WeekStringProvider { [Export("Caption")] public String OutputTitle { get { return "星期六"; } } }
這裏只是定義了一個簡單的屬性,其實在同一個類型能夠定義多個輸出,Export同時指定了一個字符串的契約名稱,這意味着任何匹配契約名稱的程序均可以使用該擴展。除此以外,咱們還能夠指定一個類型來代替字符串的契約名稱,後面會說到。咱們再定義一個輸入,即用來消費該字符串,一樣是一個簡單的屬性,不過此次添加的是Import特性:工具
// Author: TerryLee 2008.8.30 // URL: http://www.cnblogs.com/Terrylee public class Client { [Import("Caption")] public String OutputTitle { get; set; } }
如今有了輸出和輸入,就能夠在主程序中進行調用了,須要建立一個CompositionContainer容器,並添加全部的組件到該容器中,再調用它的Bind()方法,一旦調用該方法後,就可使用全部的組件了,以下代碼所示:post
// Author: TerryLee 2008.8.30 // URL: http://www.cnblogs.com/Terrylee static void Main(string[] args) { Client client = new Client(); CompositionContainer container = new CompositionContainer(); container.AddComponent<Client>(client); container.AddComponent<WeekStringProvider>(new WeekStringProvider()); container.Bind(); Console.WriteLine(client.OutputTitle); }
輸出結果以下圖所示:ui
如今咱們再定義另一個擴展程序,讓它返回一個日期字符串,以下代碼所示:spa
// Author: TerryLee 2008.8.30 // URL: http://www.cnblogs.com/Terrylee public class DateStringProvider { [Export("Caption")] public String OutputTitle { get { return DateTime.Now.ToLongDateString(); } } }
修改一下組件註冊程序,以下代碼所示:設計
// Author: TerryLee 2008.8.30 // URL: http://www.cnblogs.com/Terrylee static void Main(string[] args) { Client client = new Client(); CompositionContainer container = new CompositionContainer(); container.AddComponent<Client>(client); container.AddComponent<DateStringProvider>(new DateStringProvider()); container.Bind(); Console.WriteLine(client.OutputTitle); }
輸出結果以下圖所示:code
上面的示例中咱們是使用了命名契約,除此以外,還可使用類型契約,如定義一個字符串提供者程序的接口:
// Author: TerryLee 2008.8.30 // URL: http://www.cnblogs.com/Terrylee public interface IStringProvider { String OutputTitle { get; set; } }
如今輸出和輸入對應的修改成以下代碼:
// Author: TerryLee 2008.8.30 // URL: http://www.cnblogs.com/Terrylee public class DateStringProvider : IStringProvider { [Export(typeof(IStringProvider))] public String OutputTitle { get { return DateTime.Now.ToLongDateString(); } } }
// Author: TerryLee 2008.8.30 // URL: http://www.cnblogs.com/Terrylee public class Client { [Import(typeof(IStringProvider))] public String OutputTitle { get; set; } }
運行後能夠看到它與前面的示例效果是同樣的。
Duck Typing支持
瞭解DI的朋友可能都有這樣的疑問,其實上面的代碼就是一個依賴注入,微軟模式與實踐團隊已經開發出了Unity,爲何還須要一個MEF呢?其實MEF的定位並非DI,在前面我已經說過,它主要是用於應用程序擴展管理,下面咱們再看一個示例,它在這方面具備什麼樣的優點,看下面這段代碼:
// Author: TerryLee 2008.8.30 // URL: http://www.cnblogs.com/Terrylee [ContractType("TerryLeeCalculatro")] public interface ICalculator { int Execute(int x, int y); } public class Client { [Import(typeof(ICalculator))] public ICalculator Calculator { get; set; } }
這裏咱們定義了一個輸入,它裏面具備一個ICalculator的屬性,也就是說它須要的輸入是一個類型是ICalculator的實例。如今咱們定義輸出,以下代碼所示:
// Author: TerryLee 2008.8.30 // URL: http://www.cnblogs.com/Terrylee [Export(typeof(ICalculator))] public class SubCalculator : ICalculator { public int Execute(int x, int y) { return x - y; } }
在主函數中進行調用:
// Author: TerryLee 2008.8.30 // URL: http://www.cnblogs.com/Terrylee static void Main(string[] args) { Client client = new Client(); CompositionContainer container = new CompositionContainer(); container.AddComponent<Client>(client); container.AddComponent<SubCalculator>(new SubCalculator()); container.Bind(); Console.WriteLine(client.Calculator.Execute(1,2)); }
輸出結果以下圖所示:
如今咱們須要對該程序擴展,讓其計算結果爲兩個數相加,若是使用DI,咱們可能會想到,再編寫一個支持加法計算的類,讓其實現ICalculator接口,然而這裏咱們從新定義了一個新的接口IMyCalculator:
// Author: TerryLee 2008.8.30 // URL: http://www.cnblogs.com/Terrylee [ContractType("TerryLeeCalculatro")] public interface IMyCalculator { int Execute(int x, int y); } [Export(typeof(IMyCalculator))] public class AddCalculator : IMyCalculator { public int Execute(int x, int y) { return x + y; } }
這裏從新定義了一個新接口IMyCalculator,咱們爲它設置的契約類型和前面定義的接口ICalculator一致。而AddCalculator實現這個接口,一樣用Export標識它爲一個輸出。最後調用程序以下:
// Author: TerryLee 2008.8.30 // URL: http://www.cnblogs.com/Terrylee static void Main(string[] args) { Client client = new Client(); CompositionContainer container = new CompositionContainer(); container.AddComponent<Client>(client); container.AddComponent<AddCalculator>(new AddCalculator()); container.Bind(); Console.WriteLine(client.Calculator.Execute(1,2)); }
輸出結果以下圖所示:
你們可能已經意識到了,上面示例中的輸入須要ICalculator類型,而咱們擴展的輸出倒是IMyCalculator類型,它僅僅是與ICalculator標識爲相同的契約類型,這種方式帶來了極大的靈活性,也就是說咱們在對原有應用程序進行擴展時,並不須要與原有應用程序產生任何依賴,能夠獨立的進行擴展。
Plug-In支持
在前面的例子中,始終有一個問題沒有解決,就是當每次編寫一個擴展程序後,都須要修改代碼向CompositionContainer中註冊組件,這樣其實並無實現真正的擴展,咱們但願的擴展是Plug-In機制。在MEF對於Plug-In提供了很好的支持,它提供了DirectoryWatchingComponentCatalog類來對指定的目錄進行監視,就是說咱們定義好了輸入以後,只要把相關的輸出組件放在指定目錄中,MEF會經過反射來進行自動查找,如咱們定義這樣的一個輸入:
// Author: TerryLee 2008.8.30 // URL: http://www.cnblogs.com/Terrylee public class User { [Import("Role")] public String Role { get; set; } }
如今定義輸出,咱們把它放在一個單獨的類庫項目中:
// Author: TerryLee 2008.8.30 // URL: http://www.cnblogs.com/Terrylee public class DatabaseProvider { [Export("Role")] public String AvailableRole { get { return "Developer"; } } }
在主調用程序的目錄下,咱們建立一個Extensions的目錄,而後把相關的擴展組件都放在該目錄下,並在主調用程序中,爲DirectoryWatchingComponentCatalog實例加入Extensions目錄,這樣就避免了與具體的擴展應用程序產生依賴,以下代碼所示:
// Author: TerryLee 2008.8.30 // URL: http://www.cnblogs.com/Terrylee static void Main(string[] args) { User user = new User(); DirectoryWatchingComponentCatalog catalog = new DirectoryWatchingComponentCatalog(); catalog.AddDirectory(@"Extensions"); CompositionContainer container = new CompositionContainer(catalog.Resolver); container.AddComponent<User>(user); container.Bind(); Console.WriteLine(user.Role); Console.ReadLine(); }
運行後輸出結果以下:
對於MEF來講,Duck Typing支持以及Plug-In支持纔是它的優點所在,它不是一個簡單的DI容器,而是一個真正的管理擴展框架。固然瞭如今MEF還處於CTP階段,不少功能還不是很完善。在8月初,微軟還特地請到了Castle之父Hammett加入該項目組,擔任Program Manager,MEF的將來值得期待,更值得期待的是MEF將會爲Silverlight應用程序開發一個MEF子集,讓咱們對於Silverlight程序也可以方便的進行擴展。
Managed Extensibility Framework的官方主頁是:http://code.msdn.microsoft.com/mef。
總結
本文簡單介紹了Managed Extensibility Framework的一些使用,但願對你們有所幫助。