本文主要把MEF作为一种IOC容器进行讲解,.net中可用的IOC容器非常多,如 CastleWindsor,Unity,Autofac,ObjectBuilder,StructureMap,Spring.Net等,这些第三方工具各不相同,但功能大体都相同,大都需要事先对接口与实现进行配对(通过代码或配置文件),然后由系统自动或手动来通过接口来获得相应实现类的实例,对象实例化的工作由IOC容器自动完成。

概述   

官方说法: Managed Extensibility Framework(MEF)是.NET平台下的一个扩展性管理框架,它是一系列特性的集合,包括依赖注入(DI)等。MEF为开发人员提供了一个工具,让我们可以轻松的对应用程序进行扩展并且对已有的代码产生最小的影响,开发人员在开发过程中根据功能要求定义一些扩展点,之后扩展人员就可以使用这些扩展点与应用程序交互;同时MEF让应用程序与扩展程序之间不产生直接的依赖,这样也允许在多个具有同样的扩展需求之间共享扩展程序。

解决的问题   

MEF解决了什么呢?以往,如果一个应用程序需要支持插件方式必须要实现自己的底层并且这些插件通常是针对特有应用的,不能被其他应用所使用。实际上MEF提供了发现和组合的能力使你的应用程序可以加载扩展,为运行时的可扩展性提供了一种简单的解决方法:  MEF为宿主应用提供了一种标准的途径来暴露自身并使用外部扩展。而扩展本身是可以被不同的应用程序所使用的。而一个扩展依旧可以通过针对特定应用的方法来实现。扩展之间也可以存在依赖关系,MEF则会自动将它们按照正确的顺序进行调用。MEF还提供了一些用来定位和加载可用扩展的方法。MEF允许使用附加元数据对扩展进行标记,从而达到易于丰富的查询和筛选的目的。

工作原理

简短说一下MEF的工作原理,MEF的核心包括一个catalog和一个CompositionContainer。category用于发现扩展,而container用于协调创建和梳理依赖性。每个可组合的Part提供了一个或多个Export,并且通常依赖于一个或多个外部提供的服务或Import。每个Part管理一个实例为应用程序运行

MEF 提供一种通过“组合”隐式发现组件的方法。 MEF 组件(称为“部件-Part”)。部件以声明方式同时指定其依赖项(称为“导入-Import”)及其提供的功能(称为“导出-Export”)。MEF原理上很简单,找出有共同接口的导入、导出。然后找到把导出的实例化,赋给导入。说到底MEF就是找到合适的类实例化,把它交给导入。

如何声明一个部件-导入与导出

导出”是部件向容器中的其他部件提供的一个值,而“导入”是部件向要通过可用导出满足的容器提出的要求。 在特性化编程模型中,导入和导出是由修饰类或成员使用 Import 和Export 特性声明的。 Export 特性可修饰类、字段、属性或方法,而 Import 特性可修饰字段、属性或构造函数参数。为了使导入与导出匹配,导入和导出必须具有相同的协定。

假设有一个类MyClass,它声明了可以导入插件的类型是IMyAddin。

public class MyClass
{
[Import]
public IMyAddin MyAddin { get; set; }
}

这里有一个类,它声明为导出。类型同样为IMyAddin。

[Export(typeof(IMyAddin))]
public class MyLogger : IMyAddin { }

这样我们使用MyAddin属性的时候就可以获得到MyLogger的实例。

发现部件

MEF提供三种方式发现部件

  • AssemblyCatalog 在当前程序集发现部件。
  • DirectoryCatalog 在指定的目录发现部件。
  • DeploymentCatalog 在指定的XAP文件中发现部件(用于silverlight)

当通过不同方式发现部件的时候,还可以使用AggregateCatalog来把这些部件聚合到一起。

var catalog = new AggregateCatalog();
//把从Program所在程序集中发现的部件添加到目录中
catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
//把从指定path发现的部件添加到目录中
catalog.Catalogs.Add(new DirectoryCatalog("C:\\Users\\v-rizhou\\SimpleCalculator\\Extensions"));

如何组合部件?

在加载完部件之后,要把它们放到一个CompositionContainer容器中。

var container = new CompositionContainer(catalog)

通过调用容器的ComposeParts()方法可以把容器中的部件组合到一起。

container.ComposeParts(this);

下面我们使用一个简单的列子学习使用MEF

1、         项目结构图

注:三个项目都要添加System.ComponetModel.Composition.dll的引用。

2、         METTest项目

(1)IHelloWord.cs

为我们定义的接口,只有两个简单方法,代码如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace METTest
{
public interface IHelloWord
{
string SayHello(string str);
string SayWord(string str);
}
}

(2) HelloWord.cs

该文件继承IHelloWord接口并实现接口中的方法,HelloWord类被声明成internal防止方法在类外被引用,用[Export(typeof(IHelloWord))]修饰声明该类为导出,类型为IHelloWord,代码如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition; namespace METTest
{
[Export(typeof(IHelloWord))]
internal class HelloWord:IHelloWord
{
public string SayHello(string str)
{
return "Hello" + str;
}
public string SayWord(string str)
{
return str;
}
}
}

(3) HelloWordB.cs

该文件先不用看,下面用到了在做说明

3、         METTest1项先不管,下面用到了在做说明

4、         MEFConsoleApplication

该项目为控制台项目,添加对METTest的引用,不要添加对METTest1项目的引用。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using METTest;
using System.Reflection;
using System.ComponentModel.Composition.Hosting;
using System.IO; namespace MEFConsoleApplication
{
[Export]
class Program
{
[Import]
public IHelloWord HelloWord { get; set; } static void Main(string[] args)
{
Program p = new Program();
p.Method();
} public void Method()
{
AggregateCatalog catelog = new AggregateCatalog();
catelog.Catalogs.Add(new DirectoryCatalog(Directory.GetCurrentDirectory()));//查找部件,当前应用程序 //catelog.Catalogs.Add(new DirectoryCatalog(@"../../../MEFTest1/bin/Debug"));//这个我们通过路径找到部件
//catelog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
CompositionContainer container = new CompositionContainer(catelog);//声明容器
container.ComposeParts(this);//把容器中的部件组合到一起
//CompositionBatch Batch = new CompositionBatch();
//Batch.AddPart(this);
//container.Compose(Batch);
//HelloWord = container.GetExportedValue<IHelloWord>();//这样也可以实例化借口
Console.WriteLine(HelloWord.SayHello("eric"));
Console.WriteLine(HelloWord.SayWord("_eric")); Console.Read();
}
}
}

运行结果:

5、下面我们来看一下一个接口被多个类实例化

当一个接口被多个类实例化时,用ImportMany 声明,具体如下

[ImportMany]
public IEnumerable<IHelloWord> HelloWord { get; set; }

打开HelloWordB.cs文件继承IHelloWord,并用[Export(typeof(IHelloWord))]修饰。这样HelloWord和HelloWordB都继承了IHelloWord,并且用[Export]声明。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition; namespace METTest
{
[Export(typeof(IHelloWord))]
internal class HelloWordB:IHelloWord
{
public string SayHello(string str)
{
return "我是HelloB:" + str;
} public string SayWord(string str)
{
return "B_"+str;
}
}
}

修改MEFConsoleApplication项目的Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using METTest;
using System.Reflection;
using System.ComponentModel.Composition.Hosting;
using System.IO; namespace MEFConsoleApplication
{
[Export]
class Program
{
[ImportMany]
public IEnumerable<IHelloWord> HelloWord { get; set; } static void Main(string[] args)
{
Program p = new Program();
p.Method();
} public void Method()
{
AggregateCatalog catelog = new AggregateCatalog();
catelog.Catalogs.Add(new DirectoryCatalog(Directory.GetCurrentDirectory()));//查找部件,当前应用程序 //catelog.Catalogs.Add(new DirectoryCatalog(@"../../../MEFTest1/bin/Debug"));//这个我们通过路径找到部件
//catelog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
CompositionContainer container = new CompositionContainer(catelog);//声明容器
container.ComposeParts(this);//把容器中的部件组合到一起
//CompositionBatch Batch = new CompositionBatch();
//Batch.AddPart(this);
//container.Compose(Batch);
//HelloWord = container.GetExportedValue<IHelloWord>();//这样也可以实例化借口
//Console.WriteLine(HelloWord.SayHello("eric"));
//Console.WriteLine(HelloWord.SayWord("_eric"));
foreach (var item in HelloWord)
{
Console.WriteLine(item.SayHello("eric"));
}
Console.Read();
}
}
}

.NET自带IOC容器MEF之初体验(转)的更多相关文章

  1. .NET自带IOC容器MEF之初体验

    .NET自带IOC容器MEF之初体验   本文主要把MEF作为一种IOC容器进行讲解,.net中可用的IOC容器非常多,如 CastleWindsor,Unity,Autofac,ObjectBuil ...

  2. NET 自带IOC容器MEF指初体验

    转自:http://www.cnblogs.com/ulex/p/4186881.html IOC容器:工具较多,大体功能都相同,大都需要事先对接口与实现进行配对(通过代码或配置文件),然后由系统自动 ...

  3. .net自带的IOC容器MEF使用

    IOC能做什么 IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合.更优良的程序. 控制反转: 将控制权移交给第三方容器  new 操作 依赖注入: 在程序 ...

  4. 简单讲解Asp.Net Core自带IOC容器ServiceCollection

    一.  理解ServiceCollection之前先要熟悉几个概念:DIP.IOC.DI.Ioc容器: 二.  接下来先简单说一下几个概念问题: 1.DIP(依赖倒置原则):六大设计原则里面一种设计原 ...

  5. IOC容器MEF在MVC中的使用

    最近想把自己的网站框架用IOC改造下,经过对比,我初步选择autofac,虽然MEF不需要配置,但性能不行,autofac虽然需要自己写自动化注入,但性能非常好. 先分析下各大IOC框架的性能,分两类 ...

  6. ASP.NET Core中使用IOC三部曲(一.使用ASP.NET Core自带的IOC容器)

    前言 本文主要是详解一下在ASP.NET Core中,自带的IOC容器相关的使用方式和注入类型的生命周期. 这里就不详细的赘述IOC是什么 以及DI是什么了.. emm..不懂的可以自行百度. 目录 ...

  7. asp.net Core依赖注入(自带的IOC容器)

    今天我们主要讲讲如何使用自带IOC容器,虽然自带的功能不是那么强大,但是胜在轻量级..而且..不用引用别的库. 在新的ASP.NET Core中,大量的采用了依赖注入的方式来编写代码. 比如,在我们的 ...

  8. Spring核心原理之IoC容器初体验(2)

    本文节选自<Spring 5核心原理> 1 IoC与DI基本概念 IoC(Inversion of Control,控制反转)就是把原来代码里需要实现的对象创建.依赖,反转给容器来帮忙实现 ...

  9. 【Spring专场】「IOC容器」不看源码就带你认识核心流程以及运作原理

    这是史上最全面的Spring的核心流程以及运作原理的分析指南 [Spring核心专题]「IOC容器篇」不看繁琐的源码就带你浏览Spring的核心流程以及运作原理 [Spring核心专题]「AOP容器篇 ...

随机推荐

  1. Buildroot构建指南——工具链

    Linux系统的交叉编译工具链用来将源代码变成bin文件或者库文件的一个软件.一般大家默认工具链等于gcc或者arm-linux-gcc,但是实际上,gcc只是工具链的编译器部分,不是全部,制作一个工 ...

  2. 个人Blog小程序开发完毕

    今天忙了一天,算是把这个小程序弄好了,包括小程序前端页面.接口对接.后台系统.服务器架 设.域名备案.证书安装(现在阿里云的免费证书怎么审核要这么久啊,到现在还在审核中) 先上截图: 这个背景图片的替 ...

  3. Device-Dependent Bitmaps (DDB) 设备相关位图

    设备相关的位图(DDB)使用单一结构BITMAP结构描述.该结构的成员指定矩形区域的宽度和高度,以像素为单位;将条目从设备调色板映射到像素的数组的宽度;以及器件的颜色格式,在每个像素的颜色平面和位数方 ...

  4. String.prototype.getParm

    String.prototype.getParms=function(name){ var reg = new RegExp('(^|&)' + name + '=([^&]*)(&a ...

  5. UNIX 环境高级编程 文件和目录

    函数stat , fstat , fstatat , lstat stat函数返回与此文件有关的信息结构. fstat函数使用已打开的文件描述符(而stat则使用文件名) fstatat函数 为一个相 ...

  6. JavaScript:逻辑操作符“==”与“===”的区别

    Summary JavaScript中,逻辑操作符“===”会先检查操作数的数据类型,对不同的数据类型会返回false. 而“==”对不同类型的操作数进行比较时,会进行类型转换后再比较. Descri ...

  7. 一个MySql Sql 优化技巧分享

    有天发现一个带inner join的sql 执行速度虽然不是很慢(0.1-0.2),但是没有达到理想速度.两个表关联,且关联的字段都是主键,查询的字段是唯一索引. sql如下: SELECT p_it ...

  8. bzoj 4852 炸弹攻击

    bzoj 4852 炸弹攻击 二维平面上的最优解问题,模拟退火是一个较为优秀的近似算法. 此题确定圆心后,便可 \(O(m)\) 算出收益,且最优解附近显然也较优,是连续变化的,可以直接模拟退火. 小 ...

  9. wamp配置局域网访问

    Apache的版本是2.4.9. <Directory "D:/wamp/www/">    #    # Possible values for the Option ...

  10. Makefile常用知识点

    格式 目标:最终要去生成的文件, 定格写,后面是冒号(冒号后面是依赖) 依赖:用来生成目标的源材料 命令:加工的方法,命令前面一定是Tab, make的过程就是使用命令将依赖加工成目标的过程 工作原理 ...