分享

MEF核心笔记(4)细说MEF中的Attribute [下]

 昵称10504424 2013-04-10

MEF核心笔记(4)细说MEF中的Attribute [下]

今天,我们继续MEF的学习记录,这次内容感觉比较重要,所以,特别放到单独一篇来说,这也是MEF很有特色的地方,相信这其中的亮点,会让你感触良多的。

本篇主要有以下几节内容:

  • 部件的创建规则
  • 元数据和元数据视图
  • 部件组装通知
  • 总结

一、部件的创建规则

我们知道,在目前主流的IoC框架里,注入对象的创建都可以进行个性化配置,例如是否以单例方式创建(也就是共享一个对象,给所有需要注入的地方调用),不仅如此,如果属性【Remoting】技术的朋友应该也会接触到服务对象的实例创建,另外WCF服务对象也亦是如此,他们都有各自的创建规则。同样,MEF的部件也有它的创建规则。

涉及到MEF部件的创建规则,首先我们要看下【PartCreationPolicyAttribute】这个特性,因为部件创建的规则,主要是靠该特性来控制的。该特性只有一个属性【CreationPolicy】,类型为【System.ComponentModel.Composition.CreationPolicy】的枚举:

image

对应的【ImportAttribute】、【ImportManyAttribute】,都有一个【RequiredCreationPolicy】的属性,类型也是此枚举。从这里我们就不难看出,使用【PartCreationPolicyAttribute】我们可以指定导出部件的创建规则,通过导入的【RequiredCreationPolicy】的属性我们可以设定导入类型的创建规则。

根据【CreationPolicy】枚举的值,我们很容易就能看出其代表的意义,【Shared】代表共享部件,即单例,所有的导入都使用一个实例,如果组合引擎中没有该实例,则会创建,一旦有了,就不会再创建;【NonShared】和【Shared】相对应,即每次导入都创建一个新的实例,所有导入的实例都拥有自己唯一的状态,数据不共享;【Any】只是为了匹配导入导出,有下面一张匹配表:

导出的CreationPolicy 导入的CreationPolicy
Any Any、NonShared、Shared
NoneShared NoneShared、Any
Shared Shared、Any

只有满足上面这张表,导入导出才会匹配,下面我们做一个很简单的示例:

namespace MEFTest {
class Program {
private static CompositionContainer _container;
static void Main(string[] args) {
var catalog = new AssemblyCatalog(typeof(Program).Assembly);
_container = new CompositionContainer(catalog);
var studentManager1 = _container.GetExportedValue<StudentManager>();
var studentManager2 = _container.GetExportedValue<StudentManager>();
Console.WriteLine(object.ReferenceEquals(studentManager1, studentManager2));
Console.WriteLine(object.ReferenceEquals(studentManager1.Student,
studentManager2.Student));
while (true) {
Console.ReadLine();
}
}
}
//单例导出
[Export, PartCreationPolicy(CreationPolicy.Shared)]
public class Student {
public string Name { get; set; }
public int Age { get; set; }
}
//非单例导出
[Export, PartCreationPolicy(CreationPolicy.NonShared)]
public class StudentManager {
//默认的是 Any
[Import]
public Student Student { get; set; }
}
}

最后输出的是一个false和true,看懂了这个示例,你就看懂整个创建规则了,如果没有看懂,抱歉,只能说明,我的示例写得太烂了。

此外,我们可以预先在容器中定义导出,这样的定义不需要【Export】特性描述类型,并且这样输出的永远是单例:

_container.ComposeExportedValue<DateTime>(DateTime.Now);
Console.WriteLine(_container.GetExportedValue<DateTime>());

二、元数据和元数据视图

在MEF中,我们可以在导出部件时附加一些数据,而这些附加导出的数据就是元数据,附加导出数据的结构就是元数据视图,这是我觉得MEF中,最令人激动的功能。

导出元数据,我们使用【ExportMetadata】特性,设置该特性的【Name】和【Value】,即可导出对应的元数据,我们以示例来说:

class Program {
private static CompositionContainer _container;
static void Main(string[] args) {
var catalog = new AssemblyCatalog(typeof(Program).Assembly);
_container = new CompositionContainer(catalog);
var studentManager = _container.GetExportedValue<StudentManager>();
Console.WriteLine(studentManager.Student.Metadata.ClassName);
while (true) {
Console.ReadLine();
}
}
}
public interface IClassMetadata {
string ClassName { get; }
[DefaultValue("")]
string OtherInfo { get; }
}
[Export]
[ExportMetadata("ClassName", "一年级三班")]
public class Student {
public string Name { get; set; }
public int Age { get; set; }
}
[Export]
public class StudentManager {
[Import]
public Lazy<Student, IClassMetadata> Student { get; set; }
}

在该示例中,【IClassMetadata】即是元数据视图,我们导出的元数据必须满足该接口格式,否则【StudentManager】的【Improt】就会失败。我们在【Student】类上只导出了【ClassName】,而我们的元数据视图中还有一个【OtherInfo】的属性,这里需要注意一下,如果要提供默认值,必须标记上【DefaultValue】,否则如果不赋值(导出)的话,就匹配不了该元数据视图,也就是说,如果我们将【DefaultValue】去掉,该程序就不会正确执行了(【StudentManager】的【Improt】会失败)。

除了使用【ExportMetadata】特性导出元数据外,我们还可定义自己的导出特性来导出元数据,我们可以继承【ExportAttribute】,并且一定要有【MetadataAttribute】特性,以上的示例,我们可以改成这样:

public interface IClassMetadata {
string ClassName { get; }
string OtherInfo { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class)]
public class ExportStudent : ExportAttribute, IClassMetadata {
public ExportStudent()
: base() { }
public ExportStudent(string contractName)
: base(contractName) { }
public ExportStudent(Type contractType)
: base(contractType) { }
public ExportStudent(string contractName, Type contractType)
: base(contractName, contractType) { }
public string ClassName { get; set; }
//如果可选,必须要加上
[DefaultValue("")]
public string OtherInfo { get; set; }
}
[ExportStudent(ClassName = "一年级三班")]
public class Student {
public string Name { get; set; }
public int Age { get; set; }
}

通过继承【ExportAttribute】,我们可以实现比较强类型的编程,再也不怕字符串拼错,不过相比与【ExportMetadata】似乎是麻烦了一点点。

三、部件组装通知

这是个比较简单,但又很有用的功能,特别是在我们需要完成一些自动化的操作时(例如日志)。部件组装通知,就是当某个组件引用的部件都能满足导入,在返回已经组装完成的组件之前,先通知该组件。

若要得到通知,我们只要实现【IPartImportsSatisfiedNotification】接口即可,该接口有一个【OnImportsSatisfied】的方法,即通知组件部件组装的地方。请看简单的示例:

class Program {
private static CompositionContainer _container;
static void Main(string[] args) {
var catalog = new AssemblyCatalog(typeof(Program).Assembly);
_container = new CompositionContainer(catalog);
var studentManager = _container.GetExportedValue<MyComponent>();
while (true) {
Console.ReadLine();
}
}
}
[Export]
class MyComponent : IPartImportsSatisfiedNotification {
public void OnImportsSatisfied() {
Console.WriteLine("OK!~");
}
}

该示例很简单,但也将【IPartImportsSatisfiedNotification】淋漓尽致的体现了,由于【MyComponent】满足了组装条件,所以该通知一定能得到执行。

四、总结

这一篇和前一篇是MEF非常核心的内容,了解到这里,我们基本上已经完全可以胜任MEF的开发使用了。后续打算开发一个程序,来深入体会MEF具体使用,以及设计层面上的思想,大家一起期待吧。

作者:MKiller
出处:http://www.cnblogs.com/prinsun/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多