【转】MEF程序设计指南三:MEF中组合部件(Composable Parts)与契约(Contracts)的基本应用
按照MEF的约定,任何一个类或者是接口的实现都可以通过[System.ComponentModel.Composition.ExportAttribute] 特性将其定义为组合部件(Composable Parts),在任何需要导入组合部件的地方都可以通过在特定的组合部件对象属性上使用[System.ComponentModel.Composition.ImportAttribute ]实现组合部件的导入,两者之间通过契约(Contracts)进行通信,实际上这一步可以简单的理解为“依赖注入”,本质上就是对象的实例初始化过程。
我个人理解,凡是通过MEF的[ExportAttribute]标注的对象都可以理解为一个可进行组合的部件,包括对象和对象的属性、字段、方法、事件等;且该对象可以通过[ImportAttribute]进行导入。如下示例代码:
public class StringProvider
{
/// <summary>
/// 定义导出部件--契约为“Message”
/// </summary>
[Export("Message")]
public string Output
{
get { return "Hello World"; }
}
} public class Client
{
/// <summary>
/// 导入指定契约的部件
/// </summary>
[Import("Message")]
public string Input { get; set; } public void Print()
{
Console.WriteLine(Input);
}
}
所谓的契约也就是一种约定,或者叫做一种规则。如上代码中就使用到了契约,在对象StringProvider中就定义了一个导出部件属性(Output),并为其指定了通信契约为“Message”。这里的“Message”就是一种约定,既约定为在需要使用到这个属性的地方,都可以通过[ImportAttribute]使用契约(Message)进行部件的导入。
接下来结合《Silverlight中使用CompositionInitializer宿主MEF》一文中使用的日志记录的应用实例为基础来看看关于契约(Contracts)在较为复杂的部件中的的具体使用方法。假设定义了如下的接口与部件实现代码:
public interface ILogger
{
void WriteLog(string message);
}
[Export(typeof(ILogger))]
public class TXTLogger : ILogger
{
public void WriteLog(string message)
{
MessageBox.Show("TXTLogger>>>>>" + message);
}
} [Export(typeof(ILogger))]
public class DBLogger : ILogger
{
public void WriteLog(string message)
{
MessageBox.Show("DBLogger>>>>>" + message);
}
}
对于熟悉面向对象设计方法的人一眼就能明白,上面代码演示了一个接口具有多个实现的场景,仔细观察会发现在每个实现类上面都添加了[ExportAttribute]将其标注为导出部件,并为其添加了通信契约,而且两个实现类的通信契约都是使用的接口(ILogger)的类型参数。
这里需要注意的是在进行导入的时候如果辨别到底是使用的哪一个实现呢?在MEF中提供了一个专门用于导入多个实现的特性[System.ComponentModel.Composition.ImportManyAttribute],如上的日志实现示例就可以通过如下的方式实现多部件导入。
[ImportMany]
public IEnumerable<ILogger> Loggers { get; set; }

ImportManyAttribute特性可以将实现接口的所有实现全部组合起来。下面为使用[ImportMany]的完整示例代码:
namespace MEFTraining.CPC
{
public partial class MainPage : UserControl
{
[ImportMany]
public IEnumerable<ILogger> Loggers { get; set; } public MainPage()
{
InitializeComponent(); CompositionInitializer.SatisfyImports(this);
if (Loggers == null)
{
foreach (var logger in Loggers)
{
logger.WriteLog("Hello World");
}
}
}
} public interface ILogger
{
void WriteLog(string message);
} [Export(typeof(ILogger))]
public class TXTLogger : ILogger
{
public void WriteLog(string message)
{
MessageBox.Show("TXTLogger>>>>>" + message);
}
} [Export(typeof(ILogger))]
public class DBLogger : ILogger
{
public void WriteLog(string message)
{
MessageBox.Show("DBLogger>>>>>" + message);
}
}
}
上面介绍了如何在相同的契约下获取所有导出部件的实例,在某种情况下或许我们就只直接指导需要使用那一种那个实现方式,那么是否可以通过直接指定一个“契约名”就可以从多个实现中获取到指定的组合部件呢?答案是肯定的,接下来先看看在MEF中中对ExportAttribute和ImportAttribute的定义,源代码如下:
public class ExportAttribute : Attribute
{
public ExportAttribute() : this((string)null, (Type)null){}
public ExportAttribute(Type contractType) : this((string)null, contractType){}
public ExportAttribute(string contractName) : this(contractName, (Type)null) { }
public ExportAttribute(string contractName, Type contractType)
{
this.ContractName = contractName;
this.ContractType = contractType;
} public string ContractName { get; private set; }
public Type ContractType { get; private set; }
}
ImportAttribute同ExportAttribute一样提供了相同的重载构造函数,在将一个对象进行导出部件处理的时候可以直接通过ImportAttribute的属性给对象指定一个契约名,如本篇前面的日志组件的实现就可以修改为如下代码格式。
public interface ILogger
{
void WriteLog(string message);
}
[Export("TXT", typeof(ILogger))]
public class TXTLogger : ILogger
{
public void WriteLog(string message)
{
MessageBox.Show("TXTLogger>>>>>" + message);
}
} [Export("DB", typeof(ILogger))]
public class DBLogger : ILogger
{
public void WriteLog(string message)
{
MessageBox.Show("DBLogger>>>>>" + message);
}
}
通过为不同的导出部件指定了特定的契约名称,那么在装配部件的时候就可以通过契约名进行指定部件的装配并组合部件,为了方便调用可以提供一个服务类,将不同的实现通过不同的契约名装载组合起来以对系统提供一个统一的调用入口。以下为完整的示例代码:
public partial class MainPage : UserControl
{
/// <summary>
/// 导入日志服务对象
/// </summary>
[Import]
public LogService Service { get; set; } public MainPage()
{
InitializeComponent(); CompositionInitializer.SatisfyImports(this); Service.DBLogger.WriteLog("Hello MEF");
Service.TXTLogger.WriteLog("Hello MEF");
}
} /// <summary>
/// 聚合不同的日志记录部件,通过MEF进行组合
/// </summary>
[Export]
public class LogService
{
/// <summary>
/// 根据契约名进行部件的装配
/// </summary>
[Import("TXT")]
public ILogger TXTLogger { get; set; } [Import("DB")]
public ILogger DBLogger { get; set; }
} public interface ILogger
{
void WriteLog(string message);
} [Export("TXT", typeof(ILogger))]
public class TXTLogger : ILogger
{
public void WriteLog(string message)
{
MessageBox.Show("TXTLogger>>>>>" + message);
}
} [Export("DB", typeof(ILogger))]
public class DBLogger : ILogger
{
public void WriteLog(string message)
{
MessageBox.Show("DBLogger>>>>>" + message);
}
}
【转】MEF程序设计指南三:MEF中组合部件(Composable Parts)与契约(Contracts)的基本应用的更多相关文章
- 《MEF程序设计指南》博文汇总
<MEF程序设计指南>博文汇总 在MEF之前,人们已经提出了许多依赖注入框架来解决应用的扩展性问题,比如OSGI 实现以Spring 等等.在 Microsoft 的平台上,.NET Fr ...
- [zhuan]《MEF程序设计指南》博文汇总
http://www.cnblogs.com/beniao/archive/2010/08/11/1797537.html 在MEF之前,人们已经提出了许多依赖注入框架来解决应用的扩展性问题,比如OS ...
- [转]《MEF程序设计指南》博文汇总
在MEF之前,人们已经提出了许多依赖注入框架来解决应用的扩展性问题,比如OSGI 实现以Spring 等等.在 Microsoft 的平台上,.NET Framework 自身内部包含组件模型和 Sy ...
- [转]MEF程序设计指南
<MEF程序设计指南>博文汇总 在MEF之前,人们已经提出了许多依赖注入框架来解决应用的扩展性问题,比如OSGI 实现以Spring 等等.在 Microsoft 的平台上,.NET Fr ...
- 【转】MEF程序设计指南四:使用MEF声明导出(Exports)与导入(Imports)
在MEF中,使用[System.ComponentModel.Composition.ExportAttribute]支持多种级别的导出部件配置,包括类.字段.属性以及方法级别的导出部件,通过查看Ex ...
- MEF程序设计指南
############################################################################################## ##### ...
- 【转】MEF程序设计指南二:Silverlight中使用CompositionInitializer宿主MEF
MEF可以在传统应用程序中使用(包括桌面的Winform.控制台程序和Web的ASP.NET),也可以在RIA的Silverlight中使用.在Silverlight中只是宿主的方式有所不同,实际上在 ...
- 【转】MEF程序设计指南一:在应用程序中宿主MEF
在应用程序中宿主MEF其实非常简单,只需要创建一个组合容器对象(CompositionContainer)的实例,然后将需要组合的部件(Parts)和当前宿主程序添加到容器中即可.首先需要添加MEF框 ...
- MEF 编程指南(九):部件生命周期
理解 MEF 容器部件生命周期和实现是非常重要的事情.考虑到 MEF 关注可扩展应用程序.这变得尤为重要.生命期可以解释为期望部件的共享性(transitively, its exports) 共 ...
随机推荐
- 腾讯优图&港科大提出一种基于深度学习的非光流 HDR 成像方法
目前最好的高动态范围(HDR)成像方法通常是先利用光流将输入图像对齐,随后再合成 HDR 图像.然而由于输入图像存在遮挡和较大运动,这种方法生成的图像仍然有很多缺陷.最近,腾讯优图和香港科技大学的研究 ...
- solr 7+tomcat 8 + mysql实现solr 7基本使用(安装、集成中文分词器、定时同步数据库数据以及项目集成)
基本说明 Solr是一个开源项目,基于Lucene的搜索服务器,一般用于高级的搜索功能: solr还支持各种插件(如中文分词器等),便于做多样化功能的集成: 提供页面操作,查看日志和配置信息,功能全面 ...
- 深入分析Java Web技术内幕 修订版 pdf
百度网盘:http://pan.baidu.com/s/1slHCCw9
- [Illuminate\Database\QueryException] SQLSTATE[HY000] [1045] Access denied for user 'root'@'localhost' (using pas sword: NO) (SQL: select * from information_schema.tables where table_schema = la
[Illuminate\Database\QueryException] SQLSTATE[HY000] [1045] Access denied for user 'root'@'localhost ...
- OpenACC 梯度下降法求解线性方程的优化
▶ 书上第二章,用一系列步骤优化梯度下降法解线性方程组.才发现 PGI community 编译器不支持 Windows 下的 C++ 编译(有 pgCC 命令但是不支持 .cpp 文件,要专业版才支 ...
- rhel7配置tiger vnc详解 centos6配置安装vnc-server
参考网站:http://blog.51cto.com/xjsunjie/1963463 结合 https://blog.csdn.net/wamath/article/details/760 ...
- form表单提交参数封装
function getFormValues(element,options) { var data = {}; if(element == null || element == undefined) ...
- jap -文档 https://www.tutorialspoint.com/jpa/jpa_jpql.htm
参考网址:https://www.tutorialspoint.com/jpa/jpa_jpql.htm
- 序列化和反序列化(json 和pickle)dumps 为序列化, json为反序列化
json 可以在不同语言中进行使用 下面先介绍一下json的适用方法 import json, pickle t1 = { 'name':'alex', ', ' } t1 = json.dumps( ...
- as3 判断鼠标移动方向
import flash.events.MouseEvent; var odx:Number=mouseX; var ody:Number=mouseY; stage.addEventListener ...