组合部件通过[ExportAttribute]声明exports。在MEF中,有这么几种成员可声明exports的方式:组合部件(类)、字段、属性和方法。我们来看下ExportAttribute类的声明:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method
| AttributeTargets.Class, AllowMultiple=true, Inherited=false)] public class ExportAttribute : Attribute { }
果然,支持这四种成员,另外,在同一个目标上可应用多次该特性但是它不支持继承,如果想继承则需使用ExportAttribute的派生类InheritedExportAttribute来实现。
组合部件导出
组合部件导出被用在当需要导出它自己的时候。在前面的例子中,我们使用的都是这种方式。
[Export(typeof(IMessageSender))] class EmailSender : IMessageSender { public void Send(string msg) { Console.WriteLine("Email Sent:"+msg); } }
这里,我们需要将EmailSender部件自身导出,其实就是类级别的导出。
属性导出
组合部件也可以导出属性。属性导出有这么几个优点:
- 它允许导出像核心的CLR类型这样的密封类,或者是其它第三方类
- 它允许将导出创建方式和导出解耦。例如,导出运行时为你创建的已经存在的HttpContext部件
- 它允许在相同的组合部件中有一些相关的exports,例如一个DefaultSendersRegistry组合部件默认有一系列senders属性导出
using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Configuration; using System.Linq; using System.Text; using System.Threading.Tasks; using System.ComponentModel.Composition.Hosting; namespace ExportsDeclaring { class Program { [Import] public UsesTimeout UsesTimeout { get; set; } static void Main(string[] args) { Program p = new Program(); p.Compose(); Console.WriteLine(p.UsesTimeout.Timeout); Console.ReadKey(); } void Compose() { var container = new CompositionContainer(); container.ComposeParts(this, new UsesTimeout(),new Configuration()); } } public class Configuration { [Export("Timeout")] public int Timeout { get { return int.Parse(ConfigurationManager.AppSettings["Timeout"]); } } } [Export] public class UsesTimeout { [Import("Timeout")] public int Timeout { get; set; } } }
在上面的代码之前,需要先在配置文件中配置key为Timeout的AppSetting,例如:<add key="Timeout" value="5000"/>。
输出为:
方法导出
方法导出是用在一个部件要将它的方法导出的地方。通过在导出契约中指定委托的方式来导出方法。方法导出有下面的几点好处:
- 它允许对于要导出的方法进行细粒度的控制。例如,一个规则引擎可能倒入一系列可插拔的方法导出部件。
- 它屏蔽了调用者对于类型的了解。
- 它可以由代码生成器生成,你不需要处理其它的导出部件。
注意:由于框架的限制,方法导出最多不能超过四个参数。
在下面的例子中,MessageSender类将它的Send方法导出为一个Action<string>委托,这个Processor导入了这个相同的委托。
using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ExportsDeclaring { class Example { [Import] public Processor Processor { get; set; } static void Main() { Example e = new Example(); e.Compose(); e.Processor.Send(); Console.ReadKey(); } void Compose() { var container = new CompositionContainer(); container.ComposeParts(this, new Processor(), new MessageSender()); } } public class MessageSender { [Export(typeof(Action<string>))] public void Send(string msg) { Console.WriteLine(msg); } } [Export] public class Processor { [Import(typeof(Action<string>))] public Action<string> MessageSender { get; set; } public void Send() { MessageSender("Processed"); } } }
输出为:
你也可以使用一个简单的字符串契约来导入导出。但是,当进行方法导出时,你必须得提供一个类型或者字符串七月名称,而不能留空。
导出继承
MEF支持在一个基类或者接口中定义的导出将自动地被它的实现类继承的能力。这对于与那些想要利用MEF来发现而不是要求修改已有的客户代码的传统框架来说是很理想的。为了提供这种能力需要使用[System.ComponentModel.Composition.InheritedExportAttribute]。例如下面的ILogger接口有一个[InheritedExport],Logger实现了该接口,因此自动地导出了ILogger。
using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ExportsDeclaring { class Example1 { [Import] public ILogger Logger { get; set; } static void Main() { Example1 e = new Example1(); e.Compose(); e.Logger.Log("Logging"); Console.ReadKey(); } void Compose() { var container = new CompositionContainer(); container.ComposeParts(this, new Logger()); } } [InheritedExport] public interface ILogger { void Log(string msg); } public class Logger : ILogger { public void Log(string msg) { Console.WriteLine(msg); } } }
发现非公有的组合部件
MEF支持公有和非公有部件的发现。你不需要做任何事情来启用该行为。请注意:在并不完全受信任的环境中(包括sliverlight),非公有的组合将不被支持。