概述
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的一些使用,但愿对你们有所帮助。