Managed Extensibility Framework (MEF) 是.NET的一个组合框架,用于增强复杂应用的模块化和可扩展性。从.net framework 4.0 开始集成的组件。到目前为止,MEF的历史上最重要的应用程序是Visual Studio 2010。许多特性都是为了满足Visual Studio里的编辑器的需求,比如说,延迟加载所有东西和细粒度协定。MEF的工作原理简单来看是这样的:
现在 MEF 2.0 版本已集成于.net framework 4.5。 除了以前Import, Export等Attribute,增加的特性包括
1. 基于约定的编程模型,支持命名约定
2. 支持泛型
3. 支持多个范围
好的,先让我们来看新特性之约定的编程模型,假设这样的简单模型类图:
public class GreatEditor
{
public ILogPlugin Logger { get; set; }
public ISavePlugin Saver { get; set; }
}
public interface ILogPlugin
{
void Write(string message);
}
public class LoggerPlugin : ILogPlugin
{
public void Write(string message)
{
Console.WriteLine("Logger {0}", message);
}
}
public interface ISavePlugin
{
void Save(string message);
}
public class FilePlugin : ISavePlugin
{
public void Save(string message)
{
Console.WriteLine("FilePlugin {0}", message);
}
}
上面定义几个简单的Interface,一个名叫GreatEditor客户类引用两个interface,我们通常做法就是在属性实现依赖注入。另请你有注意这里没有使用MEF中的相关Attribute,因为下面我们使用新特性来实现,在代码是这样:
private static void FluentExportInterfaceDemo()
{
Program program = new Program();
var picker = new RegistrationBuilder();
picker.ForTypesMatching(pl => pl.Name.EndsWith("Plugin"))
.ExportInterfaces();
var catalog = new AssemblyCatalog(typeof(Program).Assembly, picker);
var container = new CompositionContainer(catalog);
container.ComposeParts(program);
var log = container.GetExportedValue<ILogPlugin>();
var save = container.GetExportedValue<ISavePlugin>();
save.Save("test");
log.Write("test");
}
注意首先您需要引用以下namespace:
using System.Reflection.Context;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Registration;
类似IOC组件,这里使用命名约定以字符串”Plugin” 结束的Type,Fluent配置风格。然后使用AssemblyCatalog装载Program的Assembly,接着GetExportedValue方法取得我们目前基类型,执行客户方法验证是否是我们想要的结果。
如果您想Import属性,那是这样的:
private static void FluentImportPropertiesDemo()
{
var picker = new RegistrationBuilder();
picker.ForTypesDerivedFrom<ILogPlugin>()
.Export<ILogPlugin>();
picker.ForType<GreatEditor>()
.Export().ImportProperties<ILogPlugin>(pi => pi.Name == "Logger",
(propInfo, builder) =>
{
builder.AllowRecomposition();
builder.RequiredCreationPolicy(CreationPolicy.NonShared);
});
var catalog = new AssemblyCatalog(typeof(Program).Assembly, picker);
var container = new CompositionContainer(catalog);
container.Compose(new CompositionBatch());
var editor = container.GetExportedValue<GreatEditor>();
editor.Logger.Write("Let it go");
}
如果想ExportMany类型,这里我们使用.net framwork自带加密类做演示:
[Export]
public class AlgorithmCollection
{
[ImportMany]
public SymmetricAlgorithm[] CryptoProviders { get; private set; }
}
public class CryptoComposer
{
public SymmetricAlgorithm Aes { get { return new AesManaged(); } }
public SymmetricAlgorithm TripleDES { get { return new TripleDESCryptoServiceProvider(); } }
public SymmetricAlgorithm RC2 { get { return new RC2CryptoServiceProvider(); } }
}
看到上面标记ImportMany的Attribute, 对应这里是SymmetricAlgorithm Array类型,接着 调用方法如下:
private static void FluentMultiExportPropertiesDemo()
{
var picker = new RegistrationBuilder();
picker.ForType<CryptoComposer>()
.ExportProperties(p => p.PropertyType == typeof(SymmetricAlgorithm));
var catalog = new AssemblyCatalog(typeof(Program).Assembly, picker);
var container = new CompositionContainer(catalog);
var foo = container.GetExportedValue<AlgorithmCollection>();
//output for debug
foo.CryptoProviders.ToList().ForEach(pr => Console.WriteLine(pr));
}
还有构造器Import的情况,假设有这样的Model:
public interface ILogger
{
void Write(string message);
}
public class Logger : ILogger
{
public void Write(string message) {
Console.WriteLine("Log with console : {0}", message);
}
}
public class Worker
{
private ILogger _logger;
public Worker(){}
public Worker(ILogger logger)
{
_logger = logger;
}
public void Execute(string message)
{
_logger.Write(message);
}
}
注意上面Work有一个有参的构造方法,调用的代码是这样的:
private static void FluentImportConstructorDemo()
{
var picker = new RegistrationBuilder();
picker.ForTypesDerivedFrom<ILogger>()
.Export<ILogger>();
picker.ForType<Worker>()
.SelectConstructor(ctors => ctors.First(info => info.GetParameters().Length == 1))
.Export();
var catalog = new AssemblyCatalog(typeof(Program).Assembly, picker);
var container = new CompositionContainer(catalog);
container.Compose(new CompositionBatch());
var worker = container.GetExportedValue<Worker>();
worker.Execute("Let it go");
}
上面的代码有注意使用SelectConstructor方法,选择有参数的构造器。
最后对于泛型的支持例子是这样的:
public class FooWithOpenGeneric
{
[Import]
public EventAggregator<int> IntAggregator { get; set; }
}
[Export]
public class EventAggregator<T>
{
public event Action<T> Notify = (item) => { };
public void Send(T item)
{
Notify(item);
}
}
如果了解泛型Generic不难看懂,调用代码:
private static void WorkWithOpenGeneric()
{
var picker = new RegistrationBuilder();
picker.ForType<FooWithOpenGeneric>()
.Export();
var catalog = new AssemblyCatalog(typeof(Program).Assembly, picker);
var container = new CompositionContainer(catalog);
container.Compose(new CompositionBatch());
var fooWithOpenGeneric = container.GetExportedValue<FooWithOpenGeneric>();
}
全部Demo方法合并在一起测试下:
static void Main(string[] args)
{
WorkWithOpenGeneric();
FluentImportConstructorDemo();
FluentExportInterfaceDemo();
FluentImportPropertiesDemo();
FluentMultiExportPropertiesDemo();
Console.Read();
}
通过上面代码演示,对MEF 2.0的两个新特性有一定了解吧,代码在 Visual Studio 2012, .net framework 4.5 测式通过。MEF在实际开发做为轻量级的组件实现基于Plugin开发,使得你的程序具有可扩展性,重用性。关于MEF更详细的内容可参考MSDN. MEF 2 实现了Plugin模式,最难得是现在集成于 .net framework 4.5 中,它还有一个子集版本支持Win 8 Metro App开发。
你可参感兴趣文章:
Pluge模式
使用Fluent配置API驱动Enterprise Library 5.0
EneterpriseLibrary5的Fluent配制API
作者:Petter Liu
出处:http://www.cnblogs.com/wintersun/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
该文章也同时发布在我的独立博客中-Petter Liu Blog。