前言:最近去了趟外地出差,介紹推廣小組開發的架構類産品。推廣對象是本部門在項目上面的同僚——1到2年工作經驗的初級程式員。在給他們介紹架構時發現很多架構設計層面的知識他們都沒有接觸過,甚至沒聽說過,這下囧了~~于是乎在想該如何跟他們解釋MEF、AOP、倉儲模式等方面的東東。本來 C#基礎系列 應該還有兩篇關于異步的沒有寫完,奈何現在要推廣這些個東西,部落客打算先介紹下項目中目前用到的些技術,異步的往後有時間再做分享。C#進階系列主要圍繞MEF、AOP、倉儲模式、Automapper、WCF等展開。本篇先來介紹下MEF的基礎知識。
1、什麼是MEF
先來看msdn上面的解釋:MEF(Managed Extensibility Framework)是一個用于建立可擴充的輕型應用程式的庫。 應用程式開發人員可利用該庫發現并使用擴充,而無需進行配置。 擴充開發人員還可以利用該庫輕松地封裝代碼,避免生成脆弱的硬依賴項。 通過 MEF,不僅可以在應用程式内重用擴充,還可以在應用程式之間重用擴充。
也有人把MEF解釋為“依賴注入”的一種方式,那麼什麼是“依賴注入”?如果這樣解釋,感覺越陷越深......根據部落客的了解,了解MEF隻需要抓住以下幾個關鍵點:
(1)字面意思,可擴充的framework,或者叫可擴充的庫。也就是說,使用MEF是為了提高程式的可擴充性。MEF會根據指定的導入導出自動去發現比對的擴充,不需要進行複雜的程式配置。
(2)在設計層面上來說,為什麼要使用MEF?為了“松耦合”!我們知道,程式設計有幾個原則,“高内聚,低耦合”就是其中一個。使用MEF可以幫助我們減少内庫之間的耦合。
當然,如果你之前壓根都沒有聽說過MEF,那麼即使看了我上面的解釋,估計也還是雲裡霧裡。沒關系,如果此刻你還有興趣,看了下面的Demo,相信你會有一個初步的認識。
2、為什麼要使用MEF:上面已經解釋過,為了程式的擴充和“松耦合”。
3、MEF的使用:
(1)MEF基礎導入導出的使用:
MEF的使用步驟主要分三步:宿主MEF并組合部件、标記對象的導出、對象的導入使用。
我們先來看一個Demo。
得到結果:

我們來分析下這段代碼:
這個方法表示添加目前Program2這個類到組合容器,為什麼要添加到組合容器?是因為隻要添加到組合容器中之後,如果該類裡面有Import,MEF才會自動去尋找對應的Export。這也就是為什麼使用MEF前必須要組合部件的原因。
這裡的[Export("chinese_hello", typeof(Person))]這個特性表示标記Chinese類的導出。
将Export轉到定義可以看到:
這裡的兩個參數:第一個表示協定名稱,如果找到名稱相同的Import,那麼就對應目前的Chinese對象;第二個參數表示要導出的類型。
這裡的chinese_hello是和Export裡面的chinese_hello對應的,由此可知,每一個[Import("chinese_hello")]這種Import一定可以找到一個對應的Export,如果找不到,程式就會報異常。當然如果這裡的Import如果改成[Import("american_hello")],那麼oPerson肯定就對應一個American對象。
通過上面的程式可以知道,我們使用[Import]這個特性,它的底層其實就是給我們初始化了一個對象。例如上面的[Import("chinese_hello")]等價于Person oPerson=new Chinese();。看到這裡可能有人就會說這個Import是多此一舉了,既然我們可以new,為什麼非要用這種奇怪的文法呢,怪别扭的。其實如果我們站在架構的層面,它的好處就是可以減少dll之間的引用。這個留在下一篇來講。
(2)MEF導入導出擴充:
按照MEF的約定,任何一個類或者是接口的實作都可以通過[System.ComponentModel.Composition.Export] 屬性将其他定義組合部件(Composable Parts),在任何需要導入組合部件的地方都可以通過在特定的組合部件對象屬性上使用[System.ComponentModel.Composition.Import ]實作部件的組合,兩者之間通過契約(Contracts)進行通信。通過上面的例子我們可以知道,對象是可以通過Import和Export來實作導入和導出的,那麼我們進一步擴充,對象的屬性、字段、方法、事件等是否也可以通過[ImportAttribute]進行導入呢?
由此說明,屬性也是可以導入導出的。原理與上類似。既然屬性可以,那麼字段就不用示範了,它和屬性應該是類似的。
下面來看看方法是否可以呢?
由此可知,方法的導入和導出是通過匿名委托的方式實作的,那麼由此類推,事件應該也是可以的,有興趣的朋友可以一試。原理和上面是一樣一樣的。
既然屬性、字段、方法、事件都可以通過Import和Export實作單一對象或變量的導入和導出,那麼如果我們想要一次導入多個對象呢?嘿嘿,微軟總是體貼的,它什麼都為我們考慮到了。我們來看看如何實作。
得到的結果為2。這裡有一點需要注意的,使用ImportMany的時候對應的Export不能有chinese_hello這類string參數,否則lstPerson的Count()為0.
(3)MEF的延遲加載
我們知道,當裝配一個元件的時候,目前元件裡面的所有的Import的變量都自動去找到對應的Export而執行了執行個體化,有些時候,出于程式效率的考慮,不需要立即執行個體化對象,而是在使用的時候才對它進行執行個體化。MEF裡面也有這種延遲加載的機制。
通過調試可知,當程式運作到var strRes = oProgram.oPerson.SayHello("李磊");這一行的時候
oPerson對象已經執行個體化了,而oPerson2.Value對象沒有執行個體化,當程式執行var strRes2 = oProgram.oPerson2.Value.SayHello("Lilei")這一句的時候,oPerson2.Value對象才進行執行個體化。這種需要在某些對程式性能有特殊要求的情況下面有一定的作用。
講到這裡,我們再來看前面關于了解MEF的兩個關鍵點:
(1)可擴充的庫:由于MEF允許通過Import的方式直接導入對象、屬性、方法等,試想,有人開發了一個元件,他們事先定義好了一系列的導出(Export),我們隻需要将它的元件引進來,使用Import的方式按照他們Export的約定導入對象即可,不用做其他複雜的配置。
(2)能更好的實作“松耦合”:比如我們項目按照面向接口程式設計的方式這樣分層:UI層、BLL接口層、BLL實作層......UI層隻需要引用BLL接口層即可,我們在BLL實作層裡面定義好Export的導出規則,然後再UI層裡面使用Import導入BLL實作層的對象即可,這樣UI層就不需要添加BLL實作層的引用。減少了dll之間的依賴。
以上就是MEF的一些基礎用法。當然在實際使用中可能不會這麼簡單,但是再複雜的用法都是在這些簡單基礎上面擴充起來的。後面還有兩篇會繼續分享MEF在項目設計層面的用法以及帶來的好處。歡迎各位拍磚斧正~~