MEF编程模型
- Contract由Contract name和Contract type组成,Contract两个参数可以省略可以implicit也可以explicit,implicit时name和type会自动推断
- 某些情况必须要指定contract name,最常见的情况是一个类export多个相同类型的成员,即使显示指定了contract name,Import和Export的contract type也要匹配
public class MyClass {
[Import("MajorRevision")]
public int MajorRevision { get; set; }
}
public class MyExportClass
{
[Export("MajorRevision")] //This one will match.
public int MajorRevision = ;
[Export("MinorRevision")]
public int MinorRevision = ;
} - 通常应该对公共类或者成员声明导出和导入,其他声明也受支持,但如果导出或导入私有成员,受保护成员或者内部成员,将会损坏部件的隔离模型,因此建议不要这样做.
- 导入的类型: 导入多个对象:如果要导入与多个contract匹配的导出,可以使用ImportMany attribute,用此attribute标记的导入始终是可选导入
- 动态导入:导入类可能需要与任何具有相同contract name的导出类匹配,此时可以使用dynamic关键字声明动态导入,contract会自动推导,如下示例代码,两个导出均与前面的导入匹配,但要注意如果这两个部件都加到了Catalog中,在执行组合时会抛异常:More than one export was found that matches the constraint:
public class MyClass {
[Import("TheString")]
public dynamic MyAddin { get; set; }
}[Export("TheString", typeof(IMyAddin))]
public class MyLogger : IMyAddin { }[Export("TheString")]
public class MyToolbar { } - 延迟导入:导入类需要间接引用导入的对象,不会立即实例化该对象,此时可以使用Lazy<T>来声明延迟导入,Lazy关键字会使对象的创建延迟到使用时创建,如下示例代码,Lazy<IMyAddin>与IMyAddin被视为相同的contract type
public class MyClass {
[Import]
public Lazy<IMyAddin> MyAddin { get; set; }
}[Export(typeof(IMyAddin))]
public class MyLogger : IMyAddin { } - 必备导入:MEF容器创建部件时默认使用部件的无参构造函数,如果要使组合引擎使用其他构造函数,必须用ImportingConstructor标记此构造函数,每个部件只有一个构造函数供组合引擎使用,如果不指定默认构造函数和ImportingConstructor attribute或者提供了多个ImportingConstructor,都会产生错误.如果C#类没有任何构造函数,则会自动生成一个默认构造函数(无参),如果有其他构造函数就不会产生默认构造函数。
public class MyClass
{
private IMyAddin _theAddin;
public MyClass() { }//This constructor will be used.
//An import with contract type IMyAddin is
//declared automatically.
[ImportingConstructor]
public MyClass(IMyAddin MyAddin)
{
_theAddin = MyAddin;
}
}
- 使用ImportingConstructor标记的构造函数的参数都会自动声明为必备导入.默认情况下会为所有参数自动推导contract type和contract name,但可以使用Import显示指定type和name:
[ImportingConstructor]
public MyClass([Import(typeof(IMySubAddin))]IMyAddin MyAddin)
{
_theAddin = MyAddin;
} - 如果使用ImportingConstructor标记的构造函数的参数是集合类型,则导出将与集合中对象的类型匹配,此时必须使用ImportMany修饰参数
- 可选导入:默认情况下如果没有与导入匹配的导出,组合会失败,此时可以使用AllowDefault property指定导入为可选,这样组合也将成功,导入属性将被设为其属性类型的默认值
public class MyClass
{
[Import(AllowDefault = true)]
public Plugin thePlugin { get; set; }
//If no matching export is avaliable,
//thePlugin will be set to null.
}
- 动态导入:导入类可能需要与任何具有相同contract name的导出类匹配,此时可以使用dynamic关键字声明动态导入,contract会自动推导,如下示例代码,两个导出均与前面的导入匹配,但要注意如果这两个部件都加到了Catalog中,在执行组合时会抛异常:More than one export was found that matches the constraint:
- public class MyClass
{
[ImportMany]
public IEnumerable<IMyAddin> MyAddin { get; set;}
} - 避免发现:为了使部件不作为目录的一部分不被发现,可以使用两种方式: 元数据:导出提供的关于其自身的额外信息,元数据可以将导出对象的属性传递到导入部件,导入部件可以使用这些数据来决定使用哪些导出,或者只收集导出的信息而不构造导出。导入必须为延迟导入才能使用元数据
元数据视图:为了使用元数据通常会声明一个称为元数据视图的接口,该接口声明声明元数据将可用,元数据视图接口必须只有属性,并且这些属性有get访问器.通常在元数据视图中命名的所有属性都是必须的,并且不会将未提供这些属性的任何导出视为匹配。DefaultValue attribute指定属性是可选的.如果未包括属性,则将为其分配使用DefaultValue 指定的默认值- 将部件类声明为abstract,抽象类不提供导出,即使使用Export标记了此类
- 使用PartNotDiscoverable attribute标记类,这样此部件不会被包括在任何目录中
[Export]
public class DataOne
{
//This part will be discovered
//as normal by the catalog.
}
[Export]
public abstract class DataTwo
{
//This part will not be discovered
//by the catalog.
}
[PartNotDiscoverable]
[Export]
public class DataThree
{
//This part will also not be discovered
//by the catalog.
}
- //元数据视图
public interface IPluginMetadata
{
//元数据
string Name { get; }
[DefaultValue()]
int Version { get; }
}[Export(typeof(IPlugin)), ExportMetadata("Name", "Logger"), ExportMetadata("Version", )]
public class Logger : IPlugin { }[Export(typeof(IPlugin)), ExportMetadata("Name", "Disk Writer")]
//Version is not required because of the DefaultValue
public class DWriter : IPlugin { }public class Addin
{
[Import]
public Lazy<IPlugin, IPluginMetadata> plugin;
} - 许多情况下元数据与ImportMany attribute结合使用,以便分析各个可用的导入并选择实例化一个导入
public class User
{
[ImportMany]
public IEnumerable<Lazy<IPlugin, IPluginMetadata>> plugins;
public IPlugin InstantiateLogger ()
{
IPlugin logger = null;
foreach (Lazy<IPlugin, IPluginMetadata> plugin in plugins)
{
if (plugin.Metadata.Name = "Logger")
logger = plugin.Value;
}
return logger;
}
} - 导入导出继承:导入始终由子类继承,而使用Export声明的导出则不会由子类继承,部件只有使用InheritedExport attribute标记自身,子类才能继承导出。InheritedExport只能作用于类级别,因此成员级别导出永远不会被继承.
如果存在于InheritedExport关联的元数据,该元数据也将被继承,子类无法修改继承的元数据,但通过使用相同contract重新声明InheritedExport,可以替换继承的元数据。注意contract必须相同否则相当于创建了一个新的导出[InheritedExport(typeof(IPlugin)), ExportMetadata("Name", "Logger"), ExportMetadata("Version", )]
public class Logger : IPlugin
{
//Exports with contract type IPlugin and
//metadata "Name" and "Version".
}
public class SuperLogger : Logger
{
//Exports with contract type IPlugin and
//metadata "Name" and "Version", exactly the same
//as the Logger class.
}
[InheritedExport(typeof(IPlugin)), ExportMetadata("Status", "Green")]
public class MegaLogger : Logger
{
//Exports with contract type IPlugin and
//metadata "Status" only. Re-declaring
//the attribute replaces all metadata.
} - 自定义导出特性:通过继承Export或InheritedExport可以扩展export attribute,以包含元数据作为特性属性,自定义attribute可以指定contract type,contract name,或任何其他元数据。自定义的attribute类必须使用MetadataAttribute attribute标记,使用MetadataAttribute标记的类中的所有属性都被视为自定义attribute中定义的元数据,如果要创建可选元数据可以使用DefaultValue attribute.
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class MyAttribute : ExportAttribute
{
public MyAttribute(string myMetadata) : base(typeof(IMyAddin))
{
MyMetadata = myMetadata;
}
public string MyMetadata { get; private set; }
}//隐式指定contract type和元数据
[MyAttribute("theData")]
public MyAddin myAddin { get; set; } - 创建策略:分两种shared和non-shared,表示策略的枚举类型CreationPolicy有三个值Shared,NonShared,Any,Any会根据上下文选用适当的CreateionPolicy,默认为Shared. 创建策略为Shared的部件将在每个导入之间共享
- 导入和导出都可以使用CreatePolicy指定部件的创建策略
- 指定Shared或NonShared的导出将仅与指定相同值或指定Any的导入匹配
- 指定Shared或NonShared的导入将仅与指定相同值或指定Any的导出匹配
- 未指定CreationPolicy默认为Any,Any的策略默认为Shar
[Export]
public class PartOne
{
//The default creation policy for an export is Any.
} public class PartTwo
{
[Import]
public PartOne partOne { get; set; } //The default creation policy for an import is Any.
//If both policies are Any, the part will be shared.
} public class PartThree
{
[Import(RequiredCreationPolicy = CreationPolicy.Shared)]
public PartOne partOne { get; set; } //The Shared creation policy is explicitly specified.
//PartTwo and PartThree will receive references to the
//SAME copy of PartOne.
} [Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class PartFour
{
//The NonShared creation policy is explicitly specified.
} public class PartFive
{
[Import]
public PartFour partFour { get; set; } //The default creation policy for an import is Any.
//Since the export's creation policy was explictly
//defined, the creation policy for this property will
//be non-shared.
} public class PartSix
{
[Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
public PartFour partFour { get; set; } //Both import and export specify matching creation
//policies. PartFive and PartSix will each receive
//SEPARATE copies of PartFour, each with its own
//internal state.
} public class PartSeven
{
[Import(RequiredCreationPolicy = CreationPolicy.Shared)]
public PartFour partFour { get; set; } //A creation policy mismatch. Because there is no
//exported PartFour with a creation policy of Shared,
//this import does not match anything and will not be
//filled.
}
- 生命周期和释放:部件应该根据自身需要实现IDisposable接口,由于容器创建并维护部件的引用,所以只有容器才能调用部件的Dispose方法,容器本身也实现了IDisposable接口。容器也提供了ReleaseExport用于释放nonshared部件.IPartImportsSatisfiedNotification包含一个名为OnImportsSatisfied的方法。如果部件实现了此接口,当部件成功导入时,就会调用此方法,某些情况可以替代构造函数执行初始化操作
MEF编程模型的更多相关文章
- MEF 编程指南(二):定义可组合部件和契约
可组合部件(Composable Parts) 在 MEF 内部可组合部件是一个可组合单元.可组合部件导出其他可组合部件需要的服务,并且从其他可组合部件导入服务.在 MEF 编程模型中,可组合部件 ...
- .Net插件编程模型:MEF和MAF[转载]
.Net插件编程模型:MEF和MAF MEF和MAF都是C#下的插件编程框架,我们通过它们只需简单的配置下源代码就能轻松的实现插件编程概念,设计出可扩展的程序.这真是件美妙的事情! 今天抽了一点时间, ...
- MEF 编程指南(十二):批量组合
MEF 容器实例并非不可变的.如果目录支持改变(像监控目录变动)或者在运行时添加/移除部件都可能发生改变.以前,你不得不做出改动并且调用 CompositionContainer 上的 Compose ...
- MEF 编程指南(七):使用目录
目录(Catalogs) MEF 特性编程模型的核心价值,拥有通过目录动态地发现部件的能力.目录允许应用程序轻松地使用那些通过 Export Attribute 注册自身的导出.下面列出 MEF ...
- JS魔法堂:深究JS异步编程模型
前言 上周5在公司作了关于JS异步编程模型的技术分享,可能是内容太干的缘故吧,最后从大家的表情看出"这条粉肠到底在说啥?"的结果:(下面是PPT的讲义,具体的PPT和示例代码在h ...
- 多线程之异步编程: 经典和最新的异步编程模型,async与await
经典的异步编程模型(IAsyncResult) 最新的异步编程模型(async 和 await) 将 IAsyncInfo 转换成 Task 将 Task 转换成 IAsyncInfo 示例1.使用经 ...
- 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换
经典的异步编程模型(IAsyncResult) 最新的异步编程模型(async 和 await) 将 IAsyncInfo 转换成 Task 将 Task 转换成 IAsyncInfo 示例1.使用经 ...
- jQuery插件编写及链式编程模型小结
JQuery极大的提高了我们编写JavaScript的效率,让我们可以愉快的编写代码,做出各种特效.大多数情况下,我们都是使用别人开发的JQuery插件,今天我们就来看看如何把我们常用的功能做出JQu ...
- 云巴:基于MQTT协议的实时通信编程模型
概要 有人常问,云巴实时通信系统到底提供了一种怎样的服务,与其他提供推送或 IM 服务的厂商有何本质区别.其实,从技术角度分析,云巴与其它同类厂商都是面向开发者的通信服务,宏观的编程模型都是大同小异, ...
随机推荐
- Optimizing web servers for high throughput and low latency
转自:https://blogs.dropbox.com/tech/2017/09/optimizing-web-servers-for-high-throughput-and-low-latency ...
- MySQL中的分页操作结合python
mysql中的分页操作结合python --分页: --方式1: ;-- 读取十行 , --从第十行读取 往后再读十行 --方式2: offset ; --从第二十行开始读取10行 -- 结合pyth ...
- 24.通过ngram分词机制实现index-time搜索推荐
一.ngram和index-time搜索推荐原理 1.什么是ngram 假设有一个单词:quick,在5种长度下的ngram情况如下: ngram length=1,q u i c k ...
- 使用androidstudio 分析内存泄漏
分析内存泄漏 http://www.jianshu.com/p/c49f778e7acf
- 00107_TCP通信
1.TCP通信的概述 (1)TCP通信同UDP通信一样,都能实现两台计算机之间的通信,通信的两端都需要创建socket对象: (2)区别在于: ①UDP中只有发送端和接收端,不区分客户端与服务器端,计 ...
- 在全局对象(不是指针)的构造函数里不要对std集合做太多操作
写MaxvisionOnvif的时候,我用个宏把每个Command类注册到了CommandBuilder里面,通过全局对象初始化实现的,如下: void CommandBuilder::Registe ...
- MySQL终端(Terminal)命令基本操作(转)
注意:MySQL数据库命令不区分大小写.但在MAC的终端,如果你想使用tab自动补全命令,那么你就必须使用大写,这样MAC的终端才会帮你补全命令,否则你按N遍tab都不会有响应. 1.数据库(data ...
- Spring Data Jpa-动态查询条件
/** * * 查看日志列表-按照时间倒序排列 * * @author: wyc * @createTime: 2017年4月20日 下午4:24:43 * @history: * @return L ...
- 优化实例- not in 和 not exists
客户运行一个SQL,非常慢.于是进行了一下改写.速度飞快,首先看一下原来的SQL. original sql SQL> explain plan for 2 select count(*) fr ...
- java枚举怎么用的
package com.pingan.property.icore.pap.common.constants; /** * */public enum UMAuthStatusEnum impleme ...