MEF学习笔记(2):定义组成部件和契约
定义组成部件和契约
组成部件
一个组成部件是MEF里的一个组成单位。组成部件给其它需要的组成部件导出服务,和从其它组成部件导入服务。在MEF的设计模型里,组成部件属于用System.ComponentModel.Composition.Import和[System.ComponentModel.Composition.Export]特性来修饰它们的导入和导出。一个组成部件应该至少包括一个导出。组成部件不是直接添加到容器就是通过使用的目录创建。MEF默认扫描的目录通过导出特性来识别。
契约
组成部件不能直依懒于另一个组成部件,而是通过依懒于字符串识别的契约。每个导出有一个契约,并且每个导入修饰它需要的契约。容器使用契约的信息去进行导入与导出间的匹配。如果没有指定契约,MEF会暗自使用该类完全规范的类名作为契约。如果一个类型被传进来,它也会使用全规范名称。
注意:默认一个类型会被传给契约,而不是一个字符串。虽然契约可以作为一个任意的字符串,但这会导致多义性。例如:“Sender"可能会被另一个项目不同类库里面的"Sender"所覆盖。所以如果你需要指定一个字符串契约,建议你契约的命名应该用命名空间来规范,包括公司名称,例如:"Contoso.Exports.Sender".
按照上面的代码说明,所有的导出应该是这样的:
namespace MEFSample { [Export] public class Exporter {...} [Export(typeof(Exporter))] public class Exporter1 {...} [Export("MEFSample.Exporter")] public class Exporter2 {...} }
接口/抽象 契约
一个普通的模式是让组成部件去导出一个接口或一个抽象类型契约而不是一个具体的类型。这就可以让导入者完全从导出的特定的实现中解耦,这种导入产生于关注点的分离。例如下面你会看到有两个都导出IMessageSender的sender实现。Notifier类导入一个IMessageSender集合,它用来调用它的Send()方法。新信息sender现在可以很轻易的添加到系统里。
[Export(typeof(IMessageSender))] public class EmailSender : IMessageSender { ... } [Export(typeof(IMessageSender))] public class TCPSender : IMessageSender { ... } public class Notifier { [ImportMany] public IEnumerable<IMessageSender> Senders {get; set;} public void Notify(string message) { foreach(IMessageSender sender in Senders) sender.Send(message); } }
契约程序集
当用MEF开发一个可扩展的应用程序的一个普通的模式是去部署一个契约程序集。一个契约程序集是一个简单的代码集,它包括了契约类型,扩展部分可以使用它来扩展你的应用。一般的这些会作为接口,但他们也可以是抽象类。另外契约程序集很可能会包括元数据视图接口,它像任何自定义MEF导出特性一样有用。
注意:你必须指定具体的接口类型(IMessageSender)作为导出,另外这个类型(EmailSender)自身也会被导出。