一、 什么是MEF
MEF(Managed Extensibility Framework)是一個用於創建可擴展的輕型應用程序的庫。 應用程序開發人員可利用該庫發現並使用擴展,而無需進行配置。 擴展開發人員還可以利用該庫輕松地封裝代碼,避免生成脆弱的硬依賴項。 通過 MEF,不僅可以在應用程序內重用擴展,還可以在應用程序之間重用擴展。(摘自MSDN)
我的理解:應用/插件均使用約定好的協議(接口)進行開發。系統將自動掃描指定文件夾,並按協議自動導入。
二、 MEF簡單例子
1、例子一
a、定義接口
public interface DemoOneInterface { void Send(string msg); }
b、使用接口
public class DemoOne { [Import] DemoOneInterface DO; public void Run() { DO.Send("DemoOne.Run"); } }
使用[Import]標記需要導入屬性(DemoOneInterface DO;),如果不標記,則MEF不會進行導入。
c、創建插件類

[Export(typeof(DemoOneInterface))] public class DemoOneInherit1 : DemoOneInterface { #region DemoOneInterface Members public void Send(string msg) { Console.WriteLine("DemoOneInherit1 send {0}", msg); } #endregion }
插件類需要使用Export標記,並且聲稱導出類型。
d、查看效果
static void Main(string[] args) { new DemoOne().Run(); Console.ReadLine(); }
原來我們使用MEF,但並沒有通知MEF去尋找插件。
我們對Main函數進行修改:
var demo = new DemoOne(); var catalog = new AggregateCatalog(); catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly)); //catalog.Catalogs.Add(new DirectoryCatalog("Addin")); //遍歷運行目錄下的Addin文件夾,查找所需的插件。 var _container = new CompositionContainer(catalog); _container.ComposeParts(demo); demo.Run();
修改后再次運行看看效果。
OK,運行起來了,和預期一樣。
2、例子二
運行例子一,沒有問題,但2個插件使用同一個的時候,會報錯。
因此我們可以為Export加入別名(contractName),並且Import的時候也指定別名,MEF就會根據別名自動進行加載。
修改后代碼如下:

public class DemoOne { [Import("2")] DemoOneInterface DO; public void Run() { DO.Send("DemoOne.Run"); } } public interface DemoOneInterface { void Send(string msg); } [Export("1",typeof(DemoOneInterface))] public class DemoOneInherit1 : DemoOneInterface { #region DemoOneInterface Members public void Send(string msg) { Console.WriteLine("DemoOneInherit1 send {0}", msg); } #endregion } [Export("2", typeof(DemoOneInterface))] public class DemoOneInherit12 : DemoOneInterface { #region DemoOneInterface Members public void Send(string msg) { Console.WriteLine("DemoOneInherit2 send {0}", msg); } #endregion }
運行效果:
3、例子三
有時我們希望一個同時使用多個插件,比如:輸出log。
這時我們可以將Import改為ImportMany,並且修改Do的類型為IEnumerable<DemoOneInterface>來導入多個插件。
修改后代碼:

public class DemoOne { [ImportMany] IEnumerable<DemoOneInterface> DoList; public void Run() { foreach (var _do in DoList) { _do.Send("DemoOne.Run"); } } } public interface DemoOneInterface { void Send(string msg); } [Export(typeof(DemoOneInterface))] public class DemoOneInherit1 : DemoOneInterface { #region DemoOneInterface Members public void Send(string msg) { Console.WriteLine("DemoOneInherit1 send {0}", msg); } #endregion } [Export(typeof(DemoOneInterface))] public class DemoOneInherit12 : DemoOneInterface { #region DemoOneInterface Members public void Send(string msg) { Console.WriteLine("DemoOneInherit2 send {0}", msg); } #endregion }
運行效果:
4、例子四
現在有很多插件使用同一個約定,但我想根據配置在同一個方法中調用某個插件。
這時我們需要使用ExportMetadata來為插件的特殊屬性進行標記。
使用到Lazy,來進行延遲加載,並且獲取插件標記的信息。(關於Lazy具體信息請自行查找)
a、新增插件描述類
public interface DemoOneInterfaceDepict { string Depict{get;} }
b、為插件定義描述

[Export(typeof(DemoOneInterface))] [ExportMetadata("Depict", "1")] public class DemoOneInherit1 : DemoOneInterface { #region DemoOneInterface Members public void Send(string msg) { Console.WriteLine("DemoOneInherit1 send {0}", msg); } #endregion } [Export(typeof(DemoOneInterface))] [ExportMetadata("Depict", "2")] public class DemoOneInherit12 : DemoOneInterface { #region DemoOneInterface Members public void Send(string msg) { Console.WriteLine("DemoOneInherit2 send {0}", msg); } #endregion }
c、修改DoList
IEnumerable<Lazy<DemoOneInterface,DemoOneInterfaceDepict>> DoList;
d、根據配置調用
public class DemoOne { [ImportMany] IEnumerable<Lazy<DemoOneInterface,DemoOneInterfaceDepict>> DoList; public void Run() { foreach (var _do in DoList.Where(item=>item.Metadata.Depict == ReadXml())) { _do.Value.Send("DemoOne.Run"); } } string ReadXml() { return "2"; } }
運行結果:
三、簡化調用
上述4個例子運行正常,但我們一直沒去在意Main函數里面的內容。
var demo = new DemoOne(); var catalog = new AggregateCatalog(); catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly)); //catalog.Catalogs.Add(new DirectoryCatalog("Addin")); //遍歷運行目錄下的Addin文件夾,查找所需的插件。 var _container = new CompositionContainer(catalog); _container.ComposeParts(demo); demo.Run();
看着頭就暈了,難道每次構造一個函數,都這么寫嗎?那不是非常痛苦?!!!
重新設計一下:
1、使用基類
public abstract class BaseClass { public BaseClass() { var catalog = new AggregateCatalog(); catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly)); var _container = new CompositionContainer(catalog); _container.ComposeParts(this); } }
修改DemoOne類繼承BaseClass
public class DemoOne : BaseClass
簡化調用
var demo = new DemoOne(); demo.Run();
運行 ok。
2、使用擴展方法
每個類都要繼承這個基類,由於C#只有單繼承,已經繼承了一個基類后,就比較麻煩。
因此衍生出第二種方法,新增擴展方法。
擴展方法
public static class ObjectExt { public static T ComposePartsSelf<T>(this T obj) where T : class { var catalog = new AggregateCatalog(); catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly)); catalog.Catalogs.Add(new DirectoryCatalog(".")); //catalog.Catalogs.Add(new DirectoryCatalog("addin")); var _container = new CompositionContainer(catalog); _container.ComposeParts(obj); return obj; } }
修改DemoOne類,新增構造函數,並且調用擴展方法
public class DemoOne { public DemoOne() { this.ComposePartsSelf(); } [ImportMany] IEnumerable<Lazy<DemoOneInterface,DemoOneInterfaceDepict>> DoList; public void Run() { foreach (var _do in DoList.Where(item=>item.Metadata.Depict == ReadXml())) { _do.Value.Send("DemoOne.Run"); } } string ReadXml() { return "2"; } }
簡化調用
var demo = new DemoOne();
demo.Run();
運行 ok。