本文主要把MEF作為一種IOC容器進行講解,.net中可用的IOC容器非常多,如 CastleWindsor,Unity,Autofac,ObjectBuilder,StructureMap,Spring.Net等,這些第三方工具各不相同,但功能大體都相同,大都需要事先對接口與實現進行配對(通過代碼或配置文件),然后由系統自動或手動來通過接口來獲得相應實現類的實例,對象實例化的工作由IOC容器自動完成。
概述
官方說法: Managed Extensibility Framework(MEF)是.NET平台下的一個擴展性管理框架,它是一系列特性的集合,包括依賴注入(DI)等。MEF為開發人員提供了一個工具,讓我們可以輕松的對應用程序進行擴展並且對已有的代碼產生最小的影響,開發人員在開發過程中根據功能要求定義一些擴展點,之后擴展人員就可以使用這些擴展點與應用程序交互;同時MEF讓應用程序與擴展程序之間不產生直接的依賴,這樣也允許在多個具有同樣的擴展需求之間共享擴展程序。
解決的問題
MEF解決了什么呢?以往,如果一個應用程序需要支持插件方式必須要實現自己的底層並且這些插件通常是針對特有應用的,不能被其他應用所使用。實際上MEF提供了發現和組合的能力使你的應用程序可以加載擴展,為運行時的可擴展性提供了一種簡單的解決方法: MEF為宿主應用提供了一種標准的途徑來暴露自身並使用外部擴展。而擴展本身是可以被不同的應用程序所使用的。而一個擴展依舊可以通過針對特定應用的方法來實現。擴展之間也可以存在依賴關系,MEF則會自動將它們按照正確的順序進行調用。MEF還提供了一些用來定位和加載可用擴展的方法。MEF允許使用附加元數據對擴展進行標記,從而達到易於豐富的查詢和篩選的目的。
工作原理
簡短說一下MEF的工作原理,MEF的核心包括一個catalog和一個CompositionContainer。category用於發現擴展,而container用於協調創建和梳理依賴性。每個可組合的Part提供了一個或多個Export,並且通常依賴於一個或多個外部提供的服務或Import。每個Part管理一個實例為應用程序運行
MEF 提供一種通過“組合”隱式發現組件的方法。 MEF 組件(稱為“部件-Part”)。部件以聲明方式同時指定其依賴項(稱為“導入-Import”)及其提供的功能(稱為“導出-Export”)。MEF原理上很簡單,找出有共同接口的導入、導出。然后找到把導出的實例化,賦給導入。說到底MEF就是找到合適的類實例化,把它交給導入。
如何聲明一個部件-導入與導出
導出”是部件向容器中的其他部件提供的一個值,而“導入”是部件向要通過可用導出滿足的容器提出的要求。 在特性化編程模型中,導入和導出是由修飾類或成員使用 Import 和Export 特性聲明的。 Export 特性可修飾類、字段、屬性或方法,而 Import 特性可修飾字段、屬性或構造函數參數。為了使導入與導出匹配,導入和導出必須具有相同的協定。
假設有一個類MyClass,它聲明了可以導入插件的類型是IMyAddin。
public class MyClass { [Import] public IMyAddin MyAddin { get; set; } }
這里有一個類,它聲明為導出。類型同樣為IMyAddin。
[Export(typeof(IMyAddin))] public class MyLogger : IMyAddin { }
這樣我們使用MyAddin屬性的時候就可以獲得到MyLogger的實例。
發現部件
MEF提供三種方式發現部件
- AssemblyCatalog 在當前程序集發現部件。
- DirectoryCatalog 在指定的目錄發現部件。
- DeploymentCatalog 在指定的XAP文件中發現部件(用於silverlight)
當通過不同方式發現部件的時候,還可以使用AggregateCatalog來把這些部件聚合到一起。
var catalog = new AggregateCatalog(); //把從Program所在程序集中發現的部件添加到目錄中 catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly)); //把從指定path發現的部件添加到目錄中 catalog.Catalogs.Add(new DirectoryCatalog("C:\\Users\\v-rizhou\\SimpleCalculator\\Extensions"));
如何組合部件?
在加載完部件之后,要把它們放到一個CompositionContainer容器中。
var container = new CompositionContainer(catalog)
通過調用容器的ComposeParts()方法可以把容器中的部件組合到一起。
container.ComposeParts(this);
下面我們使用一個簡單的列子學習使用MEF
1、 項目結構圖
注:三個項目都要添加System.ComponetModel.Composition.dll的引用。
2、 METTest項目
(1)、IHelloWord.cs
為我們定義的接口,只有兩個簡單方法,代碼如下
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace METTest { public interface IHelloWord { string SayHello(string str); string SayWord(string str); } }
(2) HelloWord.cs
該文件繼承IHelloWord接口並實現接口中的方法,HelloWord類被聲明成internal防止方法在類外被引用,用[Export(typeof(IHelloWord))]修飾聲明該類為導出,類型為IHelloWord,代碼如下
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel.Composition; namespace METTest { [Export(typeof(IHelloWord))] internal class HelloWord:IHelloWord { public string SayHello(string str) { return "Hello" + str; } public string SayWord(string str) { return str; } } }
(3) HelloWordB.cs
該文件先不用看,下面用到了在做說明
3、 METTest1項先不管,下面用到了在做說明
4、 MEFConsoleApplication
該項目為控制台項目,添加對METTest的引用,不要添加對METTest1項目的引用。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel.Composition; using METTest; using System.Reflection; using System.ComponentModel.Composition.Hosting; using System.IO; namespace MEFConsoleApplication { [Export] class Program { [Import] public IHelloWord HelloWord { get; set; } static void Main(string[] args) { Program p = new Program(); p.Method(); } public void Method() { AggregateCatalog catelog = new AggregateCatalog(); catelog.Catalogs.Add(new DirectoryCatalog(Directory.GetCurrentDirectory()));//查找部件,當前應用程序 //catelog.Catalogs.Add(new DirectoryCatalog(@"../../../MEFTest1/bin/Debug"));//這個我們通過路徑找到部件 //catelog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly())); CompositionContainer container = new CompositionContainer(catelog);//聲明容器 container.ComposeParts(this);//把容器中的部件組合到一起 //CompositionBatch Batch = new CompositionBatch(); //Batch.AddPart(this); //container.Compose(Batch); //HelloWord = container.GetExportedValue<IHelloWord>();//這樣也可以實例化借口 Console.WriteLine(HelloWord.SayHello("eric")); Console.WriteLine(HelloWord.SayWord("_eric")); Console.Read(); } } }
運行結果:
5、下面我們來看一下一個接口被多個類實例化
當一個接口被多個類實例化時,用ImportMany 聲明,具體如下
[ImportMany] public IEnumerable<IHelloWord> HelloWord { get; set; }
打開HelloWordB.cs文件繼承IHelloWord,並用[Export(typeof(IHelloWord))]修飾。這樣HelloWord和HelloWordB都繼承了IHelloWord,並且用[Export]聲明。

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel.Composition; namespace METTest { [Export(typeof(IHelloWord))] internal class HelloWordB:IHelloWord { public string SayHello(string str) { return "我是HelloB:" + str; } public string SayWord(string str) { return "B_"+str; } } }
修改MEFConsoleApplication項目的Program.cs

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel.Composition; using METTest; using System.Reflection; using System.ComponentModel.Composition.Hosting; using System.IO; namespace MEFConsoleApplication { [Export] class Program { [ImportMany] public IEnumerable<IHelloWord> HelloWord { get; set; } static void Main(string[] args) { Program p = new Program(); p.Method(); } public void Method() { AggregateCatalog catelog = new AggregateCatalog(); catelog.Catalogs.Add(new DirectoryCatalog(Directory.GetCurrentDirectory()));//查找部件,當前應用程序 //catelog.Catalogs.Add(new DirectoryCatalog(@"../../../MEFTest1/bin/Debug"));//這個我們通過路徑找到部件 //catelog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly())); CompositionContainer container = new CompositionContainer(catelog);//聲明容器 container.ComposeParts(this);//把容器中的部件組合到一起 //CompositionBatch Batch = new CompositionBatch(); //Batch.AddPart(this); //container.Compose(Batch); //HelloWord = container.GetExportedValue<IHelloWord>();//這樣也可以實例化借口 //Console.WriteLine(HelloWord.SayHello("eric")); //Console.WriteLine(HelloWord.SayWord("_eric")); foreach (var item in HelloWord) { Console.WriteLine(item.SayHello("eric")); } Console.Read(); } } }
本文參考:
http://wenku.baidu.com/view/abc72ec80508763231121273.html
http://www.cnblogs.com/techborther/archive/2012/02/06/2339877.html
點擊下載源代碼
每天學習一點點,每天進步一點點。