MEF编程指南(前两节)
在应用程序中使用MEF
在应用程序中使用MEF需要创建一个CompositionContainer的实例,向其中添加可组合的部件,将宿主应用包含进去然后组合。
以下是使用MEF需要用到的步骤:
1、 创建一个宿主类。在接下来的示例中,我们将会使用一个控制台应用,所以宿主也就是Program类了。
2、 引用System.ComponentModel.Composition程序集
3、 添加如下using语句:using System.ComponentModel.Composition;
4、 添加一个Compose()方法,它创建容器的实例并做组合的工作
5、 添加一个Run()方法,它会调用Compose() 方法
6、 在Main()方法中实例化宿主类
注意:在ASP.NET和WPF中无需这一步,因为宿主类是由运行时初始化的。
下面的代码演示了代码的样式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 using System.ComponentModel.Composition;using System.ComponentModel.Composition.Hosting;using System.Reflection;using System;public class Program { public static void Main (string [] args ) { Program p = new Program(); p.Run(); } public void Run () { Compose(); } private void Compose () { var container = new CompositionContainer(); container.ComposeParts(this ); } }
7、 定义一个或多个宿主可以导入(import)的导出(exports)。下面的代码中我们将会创建一个叫做IMessageSender的接口。我么还会定义 一个可组合组件–EmailSender类,它通过使用[System.ComponentModel.Composition.Export]特性来导出了一个IMessageSender。
1 2 3 4 5 6 7 8 9 10 public interface IMessageSender { void Send(string message); } [Export(typeof(IMessageSender))] public class EmailSender: IMessageSender { public void Send(string message) { Console.WriteLine(message); } }
8、 给宿主类添加属性,每个属性都被[ System.ComponentModel.Composition.Import]修饰。如下就是给Program类添加的一个IMessegeSender类型的导入。
1 2 [Import ] public IMessageSender MessageSender { get ; set ; }
9、 向容器中添加可组合部件。在MEF中有多种方式可以向容器中添加可组合部件。其中一种就是直接添加可组合部件的实例,还有一种更常用的方式是通过使用目录(catalog),我们稍后将会讲解这一点。
向容器中直接添加组件
在Compose()方法中通过使用ComposeParts()方法来手动添加可组合组件。下面的例子中,一个EmailSender的实例和需要导入它的Program类的实例被添加进了容器中去了。
1 2 3 4 private void Compose() { var container = new CompositionContainer(); container .ComposeParts(this , new EmailSender()); }
使用AssemblyCatalog来向容器中添加可组合组件
通过使用catalog,容器可以自动创建组件的实例而不需要我们显式的去添加它们。在Compose()方法中创建一个catalog。然后把它传入到容器的构造方 法中去。
下面的例子中,我们通过把当前程序集传入其构造方法中去来创建了一个AssemblyCatalog。我们没有手动添加EmailSender的实例,它将会被自动发 现。
1 2 3 4 5 private void Compose() { var catalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly()); var container = new CompositionContainer(catalog); container .ComposeParts(this ); }
完成上面各个步骤之后,现在代码应该是如下的样式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 using System.ComponentModel.Composition;using System.ComponentModel.Composition.Hosting;using System.Reflection;using System;public class Program { [Import ] public IMessageSender MessageSender { get ; set ; } public static void Main (string [] args ) { Program p = new Program(); p.Run(); } public void Run () { Compose(); MessageSender.Send("Message Sent" ); } private void Compose () { AssemblyCatalog catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); var container = new CompositionContainer(catalog); container.ComposeParts(this ); } } public interface IMessageSender { void Send (string message ) ; } [Export(typeof(IMessageSender)) ] public class EmailSender : IMessageSender { public void Send (string message ) { Console.WriteLine(message); } }
上面的代码编译并运行时,应用程序和它需要的导入将会被组合起来。Send()方法将会被调用,从而在控制台输出“Message Sent”。
定义可组合部件和契约
可组合部件
可组合部件可以导出其他部件需要的服务,也可以导入其他部件提供的服务。在MEF中可组合部件需要使用 System.ComponentModel.Composition.Import和 System.ComponentModel.Composition.Export来定义其导入和导出。一个可组合部件应该至少包含一个导出。可组合部件可能会 是被显式的添加进容器中去,也可能是通过使用catalog被创建的。MEF发布时带有的默认catalog可以通过Export特性来识别可组合部件。
契约
可组合部件并非是直接依赖于彼此,它们都依赖于一个契约,也就是一个标示字符串。每个导出都会有一个契约,而导入需要声明它需要哪个契约。容器通过使用契约信息来匹配 导入和导出。如果没有指明契约,MEF将会默认使用类型的全限定名作为契约。如果导出中传入了一个类型,MEF也将会使用全限定名。
下面的代码中出现的所有导出契约都是等价的。
1 2 3 4 5 6 7 8 9 10 11 namespace MEFSample { [Export] public class Exporter { ... } [Export(typeof(Exporter))] public class Exporter1 { ... } [Export("MEFSample.Exporter")] public class Exporter2 { ... } }
接口/抽象契约
通常一个可组合部件导出的都是接口或者抽象类型,而不是具体类型。比如如下的代码中,有两个类都导出了IMessageSender。Notifier类导入一组IM essageSender,并调用其中每一项的Send()方法。现在新的信息发送器可以很容易的被添加到系统中去。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 [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); } }