体验Managed Extensibility Framework精妙的设计

📅 2026/7/5 3:42:08
体验Managed Extensibility Framework精妙的设计
MEFManaged Extensibility Framework是.NET Framework 4.0一个重要的库Visual Studio 2010 Code Editor的扩展支持也是基于MEF构建的。MEF的目标是简化创建可扩展的应用程序其核心类是ComposablePart即具有组合能力的组件每一个称为ComposablePart中文可为可组合构件不过下文一直采用英文来表示这样比较贴切的组件可以组合称为Import其它组件的功能其它组件通过声明Export提供功能并且它也可以通过定义Export将其功能暴露给其它组件。ComposablePart通过组件目录ComposablePartCatalog来搜索发现需要的功能组件目录可以是一个物理文件目录、网络存储等。每一个ComposablePart还具备动态组合的能力在必要的情况下可以重新组合功能。本文将采用自底向上的思路体验一下MEF的设计思想。1 无废话MEFMEF的核心是可组合组件ComposablePart它由ComposablePartDefintion来描述和创建。每一个可组合组件通过定义ExportDefintion向其它组件提供功能通过ImportDefinition引用其它组件的功能通过Metadata来描述组件自身的信息。在创建一个ComposablePart组件后通过在组件目录ComposableCatalog搜索需要的功能实现组件组合。2 典型的MEF组合过程1创建组件目录如AssemblyCatalog2创建组合容器CompositionContainer组件容器通过组件目录搜索组件的定义3创建一个组件4从组件容器获取其它组件功能的定义然后执行匹配组合示例代码如下1 var catalog new AssemblyCatalog(Assembly.GetExecutingAssembly()); //创建一个程序集目录用于从一个程序集获取所有的组件定义2 var container new CompositionContainer(catalog); //创建一个组合容器3 var composablePart new MyComponent();4 container.ComposeParts(composablePart); //执行组合从容器中获取ExportDefinition并创建实例组合在一起5 // composablePart组合完成以供使用其原理如下图来自mef.codeplex.com官方网站3 MEF本质——组合基元组合基元是对提供具有可扩展、可组合能力的组件的“本质”支持它处于MEF的最底层是整个Framework的核心类由6个类构成如下图所示该图来自MEF白皮书白皮书有点抽象不过看起来很过瘾后面附上本人翻译的中文版。组合基元类的描述如下1ComposablePart即可组合组件是组合基元的核心类。ExportDefinitions表示该组件提供的功能的描述而ImportDefinitions则是对引用其它组件功能的约束的描述。Metadata是对组件自身的特殊标识当一个ComposablePart通过Import引用其它组件功能时元数据可能作为满足引用功能的约束的一个条件。2ExportDefinition定义ComposablePart向其它组件提供的功能这个功能使用一个ContactName和Metadata来描述。ContactName即使用这个功能的契约Metadata用于进一步描述这个功能。3ImportDefinition定义ComposablePart对其它组件提供的功能的引用即引用了另一个组件的Exports。ImportDefintion使用一个表达式来描述约束它在Constraint这个属性定义其类型为ExpressionFuncExportDefinition, bool。这个表达式用于对一个ExportDefintion做匹配判定其匹配方法如下var allExportDefs …// 从ComposablePartCatalog获取所有ExportDefinitionvar constraintDelegate Constraint.Compile(); //编译成匹配函数的代理var satisfiedExportDefs allExportDefs .FindAll(constraintDelegate); //使用匹配函数的代理来过滤所有的ExportDefs4ComposableDefinition即ComposablePart定义是ComposablePart的工厂该类定义了一类ComposablePart引用的功能、暴露的功能及其自身的元数据。引用的功能在ImportDefinitions中描述暴露的功能通过ExportDefinitions描述。而Metadata则是对组件自身的描述在MEF中一般用于在一个组件引用Import另一个组件功能时通过对另一个组件的元数据进行匹配从而来确定是否要组合另一个组件提供的功能。该类是ComposablePart的工厂提供了CreatePart方法。5ComposablePartCatalog可组合组件目录用于发现组件这些组件可能来自物理目录、网络存储等。4 如何使用MEF在上面我们描述了MEF的核心——组合基元组合基元听起来很简单很容易理解但是想直接使用组合基元来编写一个ComposablePartDefinition却不是那么容易了在MEF的实现这些类都是一些抽象类用于描述整个可扩展框架的模型。我先不想说明白MEF到底是如何来使用组合基元先看示例好了。4.1 定义ComposablePartDefinitionMEF通过引入一个基于特性的编程模型来简化ComposablePart的定义如下所示的MessageSender和Processor类均是ComposablePart定义。1 public class MessageSender2 {3 [Export(MessageSender)]4 public void Send(string message)5 {6 Console.WriteLine(message);7 }8 }9 [Export]10 public class Processor11 {12 [Import(MessageSender)]13 public Actionstring MessageSender { get; set; }14 public void Send()15 {16 MessageSender(Processed);17 }18 }4.2 创建ComposablePart1 var catalog new AssemblyCatalog(Assembly.GetExecutingAssembly()); //创建一个程序集目录用于从一个程序集获取所有的组件定义2 var assemblyCatalog new AssemblyCatalog(Assembly.GetExecutingAssembly()); //创建组件目录3 var container new CompositionContainer(assemblyCatalog); //创建组合容器4 var processorPart new Processor();5 container.ComposeParts(processorPart); //执行组合6 processorPart.Send();7 Console.ReadLine();4.3 基于特性编程模型的本质通过4.1和4.2的示例可以发现MessageSender和Processor这两个类型就是ComposablePartDefintion的实现在这两个类型我们通过Export和ImportImportMany特性来定义暴露的功能和引用的功能。CompositionContainer通过这两个类所在的程序集的组件目录来搜索所有的可组合组件定义然后在执行组合时利用这些定义创建Export对象根据Import声明的约束契约实现组件的组合。在这个编程模型里面它允许我们1使用传统OOP的类型定义来定义一个ComposablePartDefinition毋庸置疑这基本没有引入复杂的概念2使用Export/Import/ImportMany等元数据来声明组合功能非常的简单且容易理解。CompositionContainer将会在后台构建这个Part对应的ComposablePartDefinition以及组件目录其它ComposablePartDefinition在执行组合时利用Definition创建实例执行组合。5 MEF vs MAF vs Unity在刚学习MEF时经常会问一个问题那就是MEF和MAF这样的插件框架、和Unity这样的IoC框架到底有什么区别。MEF与MAFManaged Addin Framework最大不同在于前者关注使用非常简单的方式来支持具有很强灵活性的可扩展支持后者关注具有物理隔离、安全、多版本支持的插件平台架构MEF和Unity不同在于前者强调组合后者强调依赖注入。6 MEF总结MEF有3点让我非常的深刻首先是组合基元的设计其次是基于特性的编程模型最后是MEF的实现方法。组合基元是可扩展支持的本质它看起来显得非常的简单但却有能够支持强大的功能能力并且不失灵活性。“大道至简”不过“简”的程度确实因人而异MEF的“简”实在让人佩服得五体投地。这个Framework也是除了ObjectBuilder之外让我非常喜欢的框架查看其代码真是让人无比舒畅。天人之作啊这帮人的创新能力太强悍了基于特性的编程模型允许我们使用“类的定义 特性声明”的方式来定义一个具有组合能力的组件它使得我们基于MEF编写组件变得非常非常的简单这也让我再次体会到面向上下文编程方法的魅力后面我也会介绍一下我原来做过的一个基于上下文思想设计的FW和MEF的思路有点类似。MEF在实现时其顶层命名空间是System.ComponentModel.Composition底下划分了AttributeModel、Diagnostics、Hosting、Primitives、ReflectionModel命名空间。MEF的顶层命名空间定义了我们使用最多的特性底下命名空间分别用于定义特性模型、诊断支持、MEF宿主、组合基元、反射模型整体实现非常的清晰简洁看第一眼我就爱上这玩意了7 基于特性编程模型的另一个示例我原来设计了一个基于特性的智能体编程框架。首先我来简洁的描述什么是智能体。智能体就是软件代理人用软件来模拟人类的特性包括智能性、主动性、社会性、感知性等。从实现角度来看一个智能体就是一个绑定了线程、消息队列的对象这个对象用线程来模拟人类大脑用消息队列来模拟大脑记忆体。当智能体收到一条消息时其线程会接管来处理。根据上述描述大家肯定觉得使用OOP开发智能体有点麻烦。OK那下面来看看我是如何使用上下文实现智能体的。7.1 使用特性来声明一个具有感知能力和主动性的“人”1 [Agent]2 public class SomePerson3 {4 [Intelligent]5 public virtual OpenTheDoor()6 {7 // 开门主动性方法8 }9 [Sensible(Environment.Temperature)]10 public virtual OnTemperatureChanged(SensibilityContext context)11 {12 // 当感知到温度变化的响应感知性声明13 }14 }7.2 创建智能体1 var agentContainer new AgentContainer();2 var agent agentContainer.BuildSomePerson(); //在后台构建一个真正的智能体3 agent.OpenTheDoor(); //调用OpenTheDoor方法这个调用最终会转变成消息发送给真正的智能体由其本身来执行就像某人让另一人去关门一样最终将由接收到消息的人去执行关门这个动作。