一、   什么是MEF

  MEF(Managed Extensibility Framework)是一个用于创建可扩展的轻型应用程序的库。 应用程序开发人员可利用该库发现并使用扩展,而无需进行配置。 扩展开发人员还可以利用该库轻松地封装代码,避免生成脆弱的硬依赖项。 通过 MEF,不仅可以在应用程序内重用扩展,还可以在应用程序之间重用扩展。(摘自MSDN)

  我的理解:应用/插件均使用约定好的协议(接口)进行开发。系统将自动扫描指定文件夹,并按协议自动导入。

二、   MEF简单例子

1、例子一

a、定义接口

  1. public interface DemoOneInterface
  2. {
  3. void Send(string msg);
  4. }

b、使用接口

  1. public class DemoOne
  2. {
  3. [Import]
  4. DemoOneInterface DO;
  5.  
  6. public void Run()
  7. {
  8. DO.Send("DemoOne.Run");
  9. }
  10. }
  1. 使用[Import]标记需要导入属性(DemoOneInterface DO;),如果不标记,则MEF不会进行导入。

c、创建插件类

  1. [Export(typeof(DemoOneInterface))]
  2. public class DemoOneInherit1 : DemoOneInterface
  3. {
  4.  
  5. #region DemoOneInterface Members
  6.  
  7. public void Send(string msg)
  8. {
  9. Console.WriteLine("DemoOneInherit1 send {0}", msg);
  10. }
  11.  
  12. #endregion
  13. }

插件

插件类需要使用Export标记,并且声称导出类型。

d、查看效果

  1. static void Main(string[] args)
  2. {
  3. new DemoOne().Run();
  4.  
  5. Console.ReadLine();
  6. }

原来我们使用MEF,但并没有通知MEF去寻找插件。

我们对Main函数进行修改:

  1. var demo = new DemoOne();
  2.  
  3. var catalog = new AggregateCatalog();
  4.  
  5. catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
  6.  
  7. //catalog.Catalogs.Add(new DirectoryCatalog("Addin")); //遍历运行目录下的Addin文件夹,查找所需的插件。
  8.  
  9. var _container = new CompositionContainer(catalog);
  10.  
  11. _container.ComposeParts(demo);
  12.  
  13. demo.Run();

修改后再次运行看看效果。

OK,运行起来了,和预期一样。

2、例子二

运行例子一,没有问题,但2个插件使用同一个的时候,会报错。

因此我们可以为Export加入别名(contractName),并且Import的时候也指定别名,MEF就会根据别名自动进行加载。

修改后代码如下:

  1. public class DemoOne
  2. {
  3. [Import("")]
  4. DemoOneInterface DO;
  5.  
  6. public void Run()
  7. {
  8. DO.Send("DemoOne.Run");
  9. }
  10. }
  11.  
  12. public interface DemoOneInterface
  13. {
  14. void Send(string msg);
  15. }
  16.  
  17. [Export("",typeof(DemoOneInterface))]
  18. public class DemoOneInherit1 : DemoOneInterface
  19. {
  20.  
  21. #region DemoOneInterface Members
  22.  
  23. public void Send(string msg)
  24. {
  25. Console.WriteLine("DemoOneInherit1 send {0}", msg);
  26. }
  27.  
  28. #endregion
  29. }
  30.  
  31. [Export("", typeof(DemoOneInterface))]
  32. public class DemoOneInherit12 : DemoOneInterface
  33. {
  34.  
  35. #region DemoOneInterface Members
  36.  
  37. public void Send(string msg)
  38. {
  39. Console.WriteLine("DemoOneInherit2 send {0}", msg);
  40. }
  41.  
  42. #endregion
  43. }

运行效果:

3、例子三

有时我们希望一个同时使用多个插件,比如:输出log。

这时我们可以将Import改为ImportMany,并且修改Do的类型为IEnumerable<DemoOneInterface>来导入多个插件。

修改后代码:

  1. public class DemoOne
  2. {
  3. [ImportMany]
  4. IEnumerable<DemoOneInterface> DoList;
  5.  
  6. public void Run()
  7. {
  8. foreach (var _do in DoList)
  9. {
  10. _do.Send("DemoOne.Run");
  11. }
  12. }
  13. }
  14.  
  15. public interface DemoOneInterface
  16. {
  17. void Send(string msg);
  18. }
  19.  
  20. [Export(typeof(DemoOneInterface))]
  21. public class DemoOneInherit1 : DemoOneInterface
  22. {
  23.  
  24. #region DemoOneInterface Members
  25.  
  26. public void Send(string msg)
  27. {
  28. Console.WriteLine("DemoOneInherit1 send {0}", msg);
  29. }
  30.  
  31. #endregion
  32. }
  33.  
  34. [Export(typeof(DemoOneInterface))]
  35. public class DemoOneInherit12 : DemoOneInterface
  36. {
  37.  
  38. #region DemoOneInterface Members
  39.  
  40. public void Send(string msg)
  41. {
  42. Console.WriteLine("DemoOneInherit2 send {0}", msg);
  43. }
  44.  
  45. #endregion
  46. }

运行效果:

4、例子四

现在有很多插件使用同一个约定,但我想根据配置在同一个方法中调用某个插件。

这时我们需要使用ExportMetadata来为插件的特殊属性进行标记。

使用到Lazy,来进行延迟加载,并且获取插件标记的信息。(关于Lazy具体信息请自行查找)

a、新增插件描述类

  1. public interface DemoOneInterfaceDepict
  2. {
  3. string Depict{get;}
  4. }

b、为插件定义描述

  1. [Export(typeof(DemoOneInterface))]
  2. [ExportMetadata("Depict", "")]
  3. public class DemoOneInherit1 : DemoOneInterface
  4. {
  5.  
  6. #region DemoOneInterface Members
  7.  
  8. public void Send(string msg)
  9. {
  10. Console.WriteLine("DemoOneInherit1 send {0}", msg);
  11. }
  12.  
  13. #endregion
  14. }
  15.  
  16. [Export(typeof(DemoOneInterface))]
  17. [ExportMetadata("Depict", "")]
  18. public class DemoOneInherit12 : DemoOneInterface
  19. {
  20.  
  21. #region DemoOneInterface Members
  22.  
  23. public void Send(string msg)
  24. {
  25. Console.WriteLine("DemoOneInherit2 send {0}", msg);
  26. }
  27.  
  28. #endregion
  29. }

c、修改DoList

IEnumerable<Lazy<DemoOneInterface,DemoOneInterfaceDepict>> DoList;

d、根据配置调用

  1. public class DemoOne
  2. {
  3. [ImportMany]
  4. IEnumerable<Lazy<DemoOneInterface,DemoOneInterfaceDepict>> DoList;
  5.  
  6. public void Run()
  7. {
  8. foreach (var _do in DoList.Where(item=>item.Metadata.Depict == ReadXml()))
  9. {
  10. _do.Value.Send("DemoOne.Run");
  11. }
  12. }
  13.  
  14. string ReadXml()
  15. {
  16. return "";
  17. }
  18. }

运行结果:

三、简化调用

上述4个例子运行正常,但我们一直没去在意Main函数里面的内容。

  1. var demo = new DemoOne();
  2.  
  3. var catalog = new AggregateCatalog();
  4.  
  5. catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
  6.  
  7. //catalog.Catalogs.Add(new DirectoryCatalog("Addin")); //遍历运行目录下的Addin文件夹,查找所需的插件。
  8.  
  9. var _container = new CompositionContainer(catalog);
  10.  
  11. _container.ComposeParts(demo);
  12.  
  13. demo.Run();

看着头就晕了,难道每次构造一个函数,都这么写吗?那不是非常痛苦?!!!

重新设计一下:

1、使用基类

  1. public abstract class BaseClass
  2. {
  3. public BaseClass()
  4. {
  5. var catalog = new AggregateCatalog();
  6.  
  7. catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
  8.  
  9. var _container = new CompositionContainer(catalog);
  10.  
  11. _container.ComposeParts(this);
  12. }
  13. }

修改DemoOne类继承BaseClass

  1. public class DemoOne : BaseClass

简化调用

  1. var demo = new DemoOne();
  2. demo.Run();

运行 ok。

2、使用扩展方法

每个类都要继承这个基类,由于C#只有单继承,已经继承了一个基类后,就比较麻烦。

因此衍生出第二种方法,新增扩展方法。

扩展方法

  1. public static class ObjectExt
  2. {
  3. public static T ComposePartsSelf<T>(this T obj) where T : class
  4. {
  5. var catalog = new AggregateCatalog();
  6.  
  7. catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
  8. catalog.Catalogs.Add(new DirectoryCatalog("."));
  9. //catalog.Catalogs.Add(new DirectoryCatalog("addin"));
  10.  
  11. var _container = new CompositionContainer(catalog);
  12.  
  13. _container.ComposeParts(obj);
  14.  
  15. return obj;
  16. }
  17. }

修改DemoOne类,新增构造函数,并且调用扩展方法

  1. public class DemoOne
  2. {
  3. public DemoOne()
  4. {
  5. this.ComposePartsSelf();
  6. }
  7.  
  8. [ImportMany]
  9. IEnumerable<Lazy<DemoOneInterface,DemoOneInterfaceDepict>> DoList;
  10.  
  11. public void Run()
  12. {
  13. foreach (var _do in DoList.Where(item=>item.Metadata.Depict == ReadXml()))
  14. {
  15. _do.Value.Send("DemoOne.Run");
  16. }
  17. }
  18.  
  19. string ReadXml()
  20. {
  21. return "";
  22. }
  23. }

简化调用

  1. var demo = new DemoOne();
  2. demo.Run();

运行 ok。

MEF学习的更多相关文章

  1. C#可扩展编程之MEF学习笔记(五):MEF高级进阶

    好久没有写博客了,今天抽空继续写MEF系列的文章.有园友提出这种系列的文章要做个目录,看起来方便,所以就抽空做了一个,放到每篇文章的最后. 前面四篇讲了MEF的基础知识,学完了前四篇,MEF中比较常用 ...

  2. C#可扩展编程之MEF学习笔记(四):见证奇迹的时刻

    前面三篇讲了MEF的基础和基本到导入导出方法,下面就是见证MEF真正魅力所在的时刻.如果没有看过前面的文章,请到我的博客首页查看. 前面我们都是在一个项目中写了一个类来测试的,但实际开发中,我们往往要 ...

  3. C#可扩展编程之MEF学习笔记(三):导出类的方法和属性

    前面说完了导入和导出的几种方法,如果大家细心的话会注意到前面我们导出的都是类,那么方法和属性能不能导出呢???答案是肯定的,下面就来说下MEF是如何导出方法和属性的. 还是前面的代码,第二篇中已经提供 ...

  4. C#可扩展编程之MEF学习笔记(二):MEF的导出(Export)和导入(Import)

    上一篇学习完了MEF的基础知识,编写了一个简单的DEMO,接下来接着上篇的内容继续学习,如果没有看过上一篇的内容, 请阅读:http://www.cnblogs.com/yunfeifei/p/392 ...

  5. C#可扩展编程之MEF学习笔记(一):MEF简介及简单的Demo

    在文章开始之前,首先简单介绍一下什么是MEF,MEF,全称Managed Extensibility Framework(托管可扩展框架).单从名字我们不难发现:MEF是专门致力于解决扩展性问题的框架 ...

  6. C#可扩展编程之MEF学习

    MEF系列文章: C#可扩展编程之MEF学习笔记(一):MEF简介及简单的Demo C#可扩展编程之MEF学习笔记(二):MEF的导出(Export)和导入(Import) C#可扩展编程之MEF学习 ...

  7. [转]MEF学习

    MEF学习 :http://www.cnblogs.com/comsokey/p/MEF1.html C#可扩展编程之MEF学习笔记(一):MEF简介及简单的Demo C#可扩展编程之MEF学习笔记( ...

  8. MEF学习笔记

    之前公司里用到了一个叫MEF的东西,说来惭愧一直只管写代码却不曾理解MEF框架为何物,今天就来学习一下,这是一篇迟到了不知多久的博客. -------------------------------- ...

  9. MEF学习小结 z

    1.什么是MEF. MEF,全称是Managed Extensibility Framework.它是.NET Framework4.0的一个类库,其主要目的是为了创建可扩展的应用程序.按照官方说法就 ...

随机推荐

  1. NodeJs之Path

    Path模块 NodeJs提供的Path模块,使得我们可以对文件路径进行简单的操作. API var path = require('path'); var path_str = '\\Users\\ ...

  2. 高大上的微服务可以很简单,使用node写微服务

    安装 npm install m-service --save 使用 编写服务处理函数 // dir1/file1.js // 使用传入的console参数输出可以自动在日志里带上request id ...

  3. 【原】FMDB源码阅读(二)

    [原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...

  4. 谈谈一些有趣的CSS题目(二)-- 从条纹边框的实现谈盒子模型

    开本系列,讨论一些有趣的 CSS 题目,抛开实用性而言,一些题目为了拓宽一下解决问题的思路,此外,涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题中有你感觉 ...

  5. MVC CodeFirst简单的创建数据库(非常详细的步骤)

       最近在学习MVC的开发,相信有过开发经验的人初学一个新的框架时候的想法跟我一样最关心的就是这个框架如何架构,每个架构如何分工,以及最最关键的就是如何与数据库通信,再下来才是学习基础的页面设计啊等 ...

  6. C++随笔:.NET CoreCLR之GC探索(4)

    今天继续来 带大家讲解CoreCLR之GC,首先我们继续看这个GCSample,这篇文章是上一篇文章的继续,如果有不清楚的,还请翻到我写的上一篇随笔.下面我们继续: // Initialize fre ...

  7. AJAX实现登录界面

    使用php跳转界面和AJAX都可实现登录界面的跳转的登录失败对的提醒.但是,php跳转的方式 需要额外加载其他界面,用户体验差.AJAX可实现当前页面只刷新需要的数据,不对当前网页进行 重新加载或者是 ...

  8. VB.NET设置控件和窗体的显示级别

    前言:在用VB.NET开发射频检测系统ADS时,当激活已存在的目标MDI子窗体时,被其他子窗体遮住了,导致目标MDI子窗体不能显示. 这个问题怎么解决呢?网上看到一篇帖子VB.NET设置控件和窗体的显 ...

  9. beans.xml

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  10. PHP设计模式(六)原型模式(Prototype For PHP)

    原型设计模式: 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 原型设计模式简单的来说,顾名思义, 不去创建新的对象进而保留原型的一种设计模式. 缺点:原型设计模式是的最主要的缺点就 ...