如何用 MEF 扩展应用程序

  最近在写一篇关于如何扩展 Visual Studio 编辑器的文章时,用到了 MEF,因此打算写一篇文章提一下这个技术点。本篇文章并不打算详细介绍
MEF,只是一个最简单的入门,相信您在阅读本篇文章后,可以迅速开发出一个可扩展的应用程序。

简 介


  MEF(Managed Extensibility Framework),是微软推出的一款用于搭建可扩展应用程序的框架,起初是独立于 .Net
发布的,后来集成到了 .Net 4.0 中。使用该框架可以非常轻松地扩展一个已发布的应用程序的功能,连 Visual Studio IDE
中的代码编辑器窗口也采用了MEF的思想,因此大大方便了开发人员对编辑器的扩展。

  MEF 可用在任何使用 .NET Framework 的地方。可以在客户端应用程序中使用 MEF(无论应用程序使用的是 Windows
窗体、WPF,还是任何其他技术),也可以在使用 ASP.NET 的服务器应用程序中使用 MEF。

MEF的关键概念


  Import

  导入,这里建议作为一个名词来理解,即一个接受者,它可以接受外来的东东。就好比是下图中的盒子,它可以接受其它积木。

十三孔智力盒

  Export

  导出,同样建议以一个名词来理解,即一个第三方的产物。它就像上图中不同颜色的积木,这些积木不属于这个盒子,但是能被放入盒子中,来丰富盒子的功能。

积木

  Contract

  协议。要想使盒子能接受积木(比如,圆柱体只能放入圆形的接口中),那这些积木必须符合一定的形状。而这些形状就相当于是应用程序和第三方扩展之间的一个协议。

  Compose

  组合(动词),即将多个符合协议要求的部件组合在一起,构成一个功能丰富的应用程序。就好比是将不同形状的积木,按照接口的形状组合在一起。

它是如何工作的?


  MEF
会动态查找用户所指定的目录,如果发现该目录中的程序集满足协议要求,就会启动自身的组合引擎,然后根据不同的协议约定把这些扩展导入到应用程序内部。

用 MEF 实现一个最简单的可扩展应用程序


  对几个关键的概念清楚了之后,我们就可以开始实践了。最终的效果是窗体上会动态加载某一目录下的dll,并自动为每个新功能添加一个按钮,当点击按钮就会执行新的功能。

最终效果

  首先,定义一个协议。

  这个和普通定义接口没什么两样。


1     public interface IPlugin
2 {
3 string Text { get; }
4
5 void Do();
6 }

  安装接受者

  有了协议之后,就需要给应用程序安一个接受者。让这个应用程序可以通过接受者来获取第三方扩展。MEF
提供了[Import]  [ImportMany] 两种
attribute。 区别就是 Import 只能接受符合协议的一个扩展,而 ImportMany 可以接受多个,并把多个扩展放入集合中。


 1     public partial class Form1 : Form
2 {
3 public Form1()
4 {
5 InitializeComponent();
6 }
7
8 [ImportMany]
9 public IEnumerable<IPlugin> plugins;
10
11 private void Form1_Load(object sender, EventArgs e)
12 {
13 }
14
15 }


  提供一个符合协议的产物

  这个产物的生产过程其实就是实现接口的过程,唯一的区别是我们要为这个实现打上个标签,从而告诉我们的组合引擎这个东西是给接受者的。MEF 提供了
Export 来暗示这是一个可以提供给接受者的产物。


 1     [Export(typeof(IPlugin))]
2 public class MyPlugin:IPlugin
3 {
4 public string Text
5 {
6 get
7 {
8 return "This is a demo";
9 }
10 }
11
12 public void Do()
13 {
14 MessageBox.Show(Text);
15 }
16 }


  发动引擎

  万事俱备,就差发动了。前面说了引擎的主要作用就是把发现扩展,同时把这些扩展组合到应用程序中。


 1 private CompositionContainer _container;
2 private void Init()
3 {
4 try
5 {
6 MyPlugin my = new MyPlugin();
7 this._container.ComposeParts(this, my);//把扩展和实例组合在一起
8 }
9 catch (CompositionException compositionException)
10 {
11 Console.WriteLine(compositionException.ToString());
12 }
13 }

  上面的代码虽然实现了组合的功能,但是却硬把产物给编码进去了。要是每次开发了新的扩展,都得这样修改应用程序代码,那就完全没有使用MEF的必要了,而且也违反了开放封闭的设计原则。

  把上面的代码改一改。


 1 private CompositionContainer _container;
2 private void Init()
3 {
4 //设置目录,让引擎能自动去发现新的扩展
5 var catalog = new AggregateCatalog();
6 catalog.Catalogs.Add(new DirectoryCatalog("D:\\plugin\\"));
7
8 //创建一个容器,相当于是生产车间
9 _container = new CompositionContainer(catalog);
10
11 //调用车间的ComposeParts把各个部件组合到一起
12 try
13 {
14 this._container.ComposeParts(this);//这里只需要传入当前应用程序实例就可以了,其它部分会自动发现并组装
15 }
16 catch (CompositionException compositionException)
17 {
18 Console.WriteLine(compositionException.ToString());
19 }
20 }

  上面的代码会自动去发现扩展,然后加入到应用程序中来。你要做的只是把新扩展的程序集放入 D:\\plugin
目录中就可以了。是不是很方便呢?

参考资源

  Managed Extensibility Framework (MEF)

  本文源代码下载

本文来源于 《如何用 MEF 扩展应用程序》

时间: 2024-10-14 16:35:07

如何用 MEF 扩展应用程序的相关文章

【开源】前端练手笔记,Chrome扩展应用程序(html+CSS+JS) (1)

项目名称:github-notification 项目地址:https://github.com/wuchangming/github-notification 说明:本人打算抽时间学习前端(html + css +js),选择Chrome扩展应用程序制作一个简单练手的项目.避免中途放弃在此立字为证! 有一起的兄弟吗? 项目简介:一款开源的,关于Github通知和监控个人项目的Chrome扩展应用程序(其实就是想监控下github上某个项目的star数,fork数等等.当然大家有什么想法也可以一

如何用苹果终端运行程序

1.vim xx.c 新建文件 2.进去之后点i 再输入C代码 3.按ESC退出   shift+:  再输入wq,可退出C编译 4.输入 cc xx.c可进行编译连接 5.输入./a.out 输出运行结果 其他运行小指令 rm xx.c 清除 ls   文件夹 pwd 该环境的运行目录 cd xx  跳转到xx文件夹 wq保存并退出 如何用苹果终端运行程序

第3章 如何用DAP仿真器下载程序

第3章     如何用DAP仿真器下载程序 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege 3.1 仿真器简介 本书配套的仿真器为Fire-Debugger,遵循ARM公司的CMSIS-DAP标准,支持所有基于Cortex内核的单片机,常见的M3.M4和M7都可以完美支持,其外观见图 31. Fire-Debugger支持下载和在线仿真程序,支持XP/WIN7/WIN8/WIN

如何用API从应用程序到资源管理器拖放文件??????

注册表是为Windows NT和Windows95 中所有32位硬件/驱动和32位应用程序设计的数据文件.16位驱动在Windows NT下无法工作,所以所有设备都通过注册表来控制,一般这些是通过BIOS来控制的.在Win9x下,16位驱动会继续以实模式方式设备工作,它们使用 system.ini来控制.16位应用程序会工作在NT或者Win9x 下,它们的程序仍然会参考win.ini和system.ini文件获得信息和控制. 在没有注册表的情况下,操作系统不会获得必须的信息来运行和控制附属的设备

VSCode的Python扩展下程序运行的几种方式与环境变量管理

在VSCode中编写Python程序时,由于有些地方要使用环境变量,但是发现设置的环境变量有时不起作用,花了点时间研究了一下,过程不表,直接说结论. 首先,环境变量的设置,Python扩展中有三种方式: 直接设置系统环境变量,或在使用命令行启动VSCode时临时先设置环境变量.这种方式设置的环境变量在所有的运行方式下起效,但由于多个工程所用的环境变量不同,每次设置太麻烦,使用bash脚本或批处理我也觉得不爽,所以没有采用. 通过 terminal.integrated.env.windows /

linux下如何用GDB调试c++程序

原文地址:http://blog.csdn.net/wfdtxz/article/details/7368357 GDB 是GNU开源组织发布的一个强大的UNIX下的程序调试工具.或许,各位比较喜欢那种图形界面方式的,像VC.BCB等IDE的调试,但如果你是在 UNIX平台下做软件,你会发现GDB这个调试工具有比VC.BCB的图形化调试器更强大的功能.所谓“寸有所长,尺有所短”就是这个道理. 一般来说,GDB主要帮忙你完成下面四个方面的功能: 启动你的程序,可以按照你的自定义的要求随心所欲的运行

第二节:使用反射构建动态可扩展应用程序

众所周知,元数据时是用一系列表来存储的.生成一个程序集或模块时,编译器会创建一个类型定义表,一个字段定义表,一个方法定义表以及其他表.利用System.Reflection命名空间中包含的一些类型,可以写代码来反射这些元数据.实际上,这个命名空间中的类型为程序集或模块中包含的元数据创建了一个对象模型. 利用这个对象模型中的类型,可轻松枚举一个类型定义元数据表中的所有类型.然后,针对每个类型,都可以获取它的基类型,它实现的一些接口以及与类型关联的一些标志.利用System.Reflection命名

如何用Deeper打开Mac程序里的显示完整工具栏?

怎样用Deeper打开Mac程序里的显示完整工具栏?这个问题看似很简单,但是如果我们不能掌握其中的小技巧,就会花费我们双倍甚至更多的时间.所以,今天小编给大家带来快速用Deeper打开Mac程序里的显示完整工具栏的小技巧,希望对大家有所帮助! 一.首先打开Deeper软件,在软件主页面右上角单击“程序”图标,在打开的“程序”页面找到“屏幕共享”栏.如下图所示: 二.单击“屏幕共享”里“显示完整工具栏”选项(该选项前对勾为蓝色即可),如下图所示: 以上就是MacDown小编为您带来的快速用Deep

centos服务器如何用命令查看哪个程序内存占用情况

1.free -m只能查看内存总量情况 2.top M ( 注意M是大写) 3.ps aux|head -1; ps aux | sort -k4nr | head -10