MEF框架学习之旅(四)导入导出(上)
MEF中使用导出与导入,实质上就是对一个对象的实例化的过程,通过MEF的特性降低了对象的直接依赖,从而让系统的设计达到一种高灵活、高扩展性的效果。
(本篇内容来自msdn)
导入的类型
MEF 支持若干导入类型,其中包括动态导入、延迟导入、必备导入和可选导入。
动态导入
在某些情况下,导入类可能需要与具有特定协定名称的任何类型的导出匹配。 在这种情况下,类可以声明动态导入。 下面的导入与具有协定名称“TheString”的任何导出匹配。
代码段
public class MyClass { [Import("TheString")] public dynamic MyAddin { get; set; } }
如果协定类型从 dynamic 关键字推断而出,则它将与任何协定类型匹配。 在这种情况下,导入应始终指定要依据其进行匹配的协定名称。 (如果未指定协定名称,则会将导入视为未与导出匹配。)下面的两个导出均将与前面的导入匹配。
代码段
[Export("TheString", typeof(IMyAddin))] public class MyLogger : IMyAddin { } [Export("TheString")] public class MyToolbar { }
显然,导入类必须准备处理任意类型的对象。
延迟导入
在具体的设计开发中,存在着某些对象是不需要在系统运行或者的附属对象初始化的时候进行实例化的,仅仅只需要在需要使用到他的时候才会进行实例化,从系统的上来说这也是提高系统性能的一种可行的实现方式,这种方式就可以理解为对象的迟延初始化,或者叫迟延加载。在这种情况下,类可以通过使用协定类型 Lazy<T> 来声明延迟导入。 下面的导入属性声明一个延迟导入。
代码段
public class MyClass { [Import] public Lazy<IMyAddin> MyAddin { get; set; } }
从组合引擎的角度来看,协定类型 Lazy<T> 将被视为与协定类型 T 相同。 因此,前面的导入将与下面的导出匹配。
代码段
[Export(typeof(IMyAddin))] public class MyLogger : IMyAddin { }
可在 Import 特性中为延迟导入指定协定名称和协定类型。
必备导入
导出的 MEF 部件通常由组合引擎创建,以响应填写匹配的导入的直接请求或需求。 默认情况下,在创建部件时,组合引擎将使用无参数的构造函数。 若要使该引擎使用其他构造函数,您可以用 ImportingConstructor 特性标记它。
每个部件都可能只有一个供组合引擎使用的构造函数。 如果不提供默认构造函数和 ImportingConstructor 特性,或者提供多个 ImportingConstructor 特性,将会产生错误。
为了填充用 ImportingConstructor 特性标记的构造函数的参数,所有这些参数均将自动声明为导入。 这样可以方便地声明在部件初始化过程中使用的导入。 下面的类使用 ImportingConstructor 来声明导入。
代码段
public class MyClass { private IMyAddin _theAddin; //Default constructor will NOT be //used because the ImportingConstructor //attribute is present. public MyClass() { } //This constructor will be used. //An import with contract type IMyAddin is //declared automatically. [ImportingConstructor] public MyClass(IMyAddin MyAddin) { _theAddin = MyAddin; } }
默认情况下,ImportingConstructor 特性为所有参数导入使用推断出的协定类型和协定名称。 可以通过用 Import 特性修饰参数来重写此行为,这些特性随后可显式定义协定类型和协定名称。 下面的代码演示一个使用此语法来导入派生类(而不是父类)的构造函数。
代码段
[ImportingConstructor] public MyClass([Import(typeof(IMySubAddin))]IMyAddin MyAddin) { _theAddin = MyAddin; }
在使用集合参数时应特别小心。 例如,如果您使用类型为 IEnumerable<int> 的参数在构造函数上指定 ImportingConstructor,则导入将与类型为 IEnumerable<int> 的单个导出(而不是类型为 int 的一组导入)匹配。 若要与类型为 int 的一组导出匹配,您必须用 ImportMany 特性修饰参数。
由 ImportingConstructor 特性声明为导入的参数也标记为必备导入。 MEF 通常允许导出和导入形成循环。 举例来说,循环是指对象 A 导入对象 B,后者反过来又导入对象 A。 在一般情况下,循环不会成为问题,并且组合容器将正常构造两个对象。
当部件的构造函数需要导入的值时,该对象将无法参与循环。 如果对象 A 要求在其自身可构造之前构造对象 B,并且对象 B 导入对象 A,则循环将无法解决,并将发生组合错误。 因此,在构造函数参数上声明的导入为必备导入,必须先满足这些导入,之后才能使用需要这些导入的对象中的任何导出。
可选导入
Import 特性指定部件正常运行的要求。 如果导入无法得到满足,则该部件的组合将失败,并且部件将不可用。
可通过使用 AllowDefault 属性指定导入为可选。 在这种情况下,即使导入未与任何可用的导出匹配,组合也将成功,并且导入属性将设置为其属性类型的默认值Default(T)( 对于对象属性为 null,对于布尔值为 false,对于数值属性为零。)下面的类使用可选导入。
代码段
public class MyClass { [Import(AllowDefault = true)] public Plugin thePlugin { get; set; } //If no matching export is avaliable, //thePlugin will be set to null. }
下面是导入参数的一个例子:
代码段
[Export] public class OrderController { private ILogger _logger; [ImportingConstructor] public OrderController([Import(AllowDefault = true)] ILogger logger) { if (logger == null) logger = new DefaultLogger(); _logger = logger; } }
OrderController可选导入logger。如果logger不存在,它会设置private _logger为DefaultLogger 实例,否则它会使用导入的logger。
相关阅读: