.Net插件编程模型:MEF和MAF[转载]
.Net插件编程模型:MEF和MAF
MEF和MAF都是C#下的插件编程框架,我们通过它们只需简单的配置下源代码就能轻松的实现插件编程概念,设计出可扩展的程序。这真是件美妙的事情!
今天抽了一点时间,看了看MEF的例子,比较简单,有时间会整理一个简单的例子出来。
简单的说,MEF使用了两个标注实现依赖注入。
[Export]可以指定输出的类或者属性
[Import]可以指定引入
通过Export,可以很轻松地将类或者属性输出到依赖注入的容器,再通过Export引入,对象的创建和组装,完全可以通过容器实现,并且可以通过[PartCreationPolicy(CreationPolicy.Shared)]标注,指定为单例模式。
通过依赖注入,可以不必编写单例模式的代码。
MEF(Managed Extensibility Framework)
MEF的工作原理大概是这样的:首先定义一个接口,用这个接口来约束插件需要具备的职责;然后在实现接口的程序方法上面添加反射标记“[Export()]”将实现的内容导出;最后在接口的调用程序中通过属性将插件加载进来。我们还是用代码来描述吧:
1. 定义一个接口:
- /*
- 作者:GhostBear
- 博客:http://blog.csdn.net/ghostbear
- 简介:该节主要学习.net下的插件编程框架MEF(managed extensibility framework)
- */
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Windows;
- namespace chapter28_simplecontract
- {
- public interface ICalculator
- {
- IList<IOperation> GetOperations();
- double Operate(IOperation operation, double[] operands);
- }
- public interface IOperation
- {
- string Name { get; }
- int NumberOperands { get; }
- }
- public interface ICaculatorExtension
- {
- string Title { get; }
- string Description { get; }
- FrameworkElement GetUI();
- }
- }
2. 实现定义的接口(部分一)
- /*
- 作者:GhostBear
- 博客:http://blog.csdn.net/ghostbear
- */
- [Export(typeof(ICalculator))]
- public class Caculator:ICalculator
- {
- public IList<IOperation> GetOperations()
- {
- return new List<IOperation>(){
- new Operation{ Name="+",NumberOperands=2},
- new Operation{Name="-",NumberOperands=2},
- new Operation{Name="*",NumberOperands=2},
- new Operation{Name="/",NumberOperands=2}
- };
- }
- public double Operate(IOperation operation, double[] operands)
- {
- double result=0;
- switch (operation.Name)
- {
- case "+":
- result = operands[0] + operands[1];
- break;
- case "-":
- result = operands[0] - operands[1];
- break;
- case "*":
- result = operands[0] * operands[1];
- break;
- case "/":
- result = operands[0] / operands[1];
- break;
- default:
- throw new Exception("not provide this method");
- }
- return result;
- }
- }
- public class Operation:IOperation
- {
- public string Name
- {
- get;
- internal set;
- }
- public int NumberOperands
- {
- get;
- internal set;
- }
- }
实现定义的接口(部分二)
- /*
- 作者:GhostBear
- 博客:http://blog.csdn.net/ghostbear
- */
- [Export(typeof(ICalculator))]
- public class Caculator : ICalculator
- {
- public IList<IOperation> GetOperations()
- {
- return new List<IOperation>(){
- new Operation{ Name="+",NumberOperands=2},
- new Operation{Name="-",NumberOperands=2},
- new Operation{Name="*",NumberOperands=2},
- new Operation{Name="/",NumberOperands=2},
- new Operation{Name="%",NumberOperands=2},
- new Operation{Name="**",NumberOperands=1},
- };
- }
- public double Operate(IOperation operation, double[] operands)
- {
- double result = 0;
- switch (operation.Name)
- {
- case "+":
- result = operands[0] + operands[1];
- break;
- case "-":
- result = operands[0] - operands[1];
- break;
- case "*":
- result = operands[0] * operands[1];
- break;
- case "/":
- result = operands[0] / operands[1];
- break;
- case "%":
- result=operands[0]%operands[1];
- break;
- case "**":
- result=operands[0]*operands[0];
- break;
- default:
- throw new Exception("not provide this method");
- }
- return result;
- }
- }
- public class Operation : IOperation
- {
- public string Name
- {
- get;
- internal set;
- }
- public int NumberOperands
- {
- get;
- internal set;
- }
- }
分析:
标记“[Export(typeof(ICalculator))]”声明表达的意思是:这个类可以编译为插件,并能放入插件容器“ICalculator”中。这里需要注意的是:部分一和部分二的代码分布在不同的程序集中。导出的插件不一定必须是以类的形式,也可以是方法。
通过导出方法来生成插件:
- /*
- 作者:GhostBear
- 博客:http://blog.csdn.net/ghostbear
- */
- public class Bomb
- {
- [Export("Bomb")]
- public void Fire()
- {
- Console.WriteLine("you are dead!!!");
- }
- }
插件的调用者:
- /*
- 作者:GhostBear
- 博客:http://blog.csdn.net/ghostbear
- * 简介:该节主要学习.net下的插件编程框架MEF(managed extensibility framework)
- */
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.ComponentModel.Composition;
- using System.ComponentModel.Composition.Hosting;
- using chapter28_simplecontract;
- namespace chapter28
- {
- class Program
- {
- [ImportMany(typeof(ICalculator))]
- public IEnumerable<ICalculator> Calculators { get; set; }
- [Import("Bomb")]
- public Action Bomb { get; set; }
- static void Main(string[] args)
- {
- Program pro = new Program();
- pro.Run();
- pro.Run2();
- }
- public void Run()
- {
- var catalog = new DirectoryCatalog("c:\\plugins");
- var container = new CompositionContainer(catalog);
- try
- {
- container.ComposeParts(this);
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- return;
- }
- ICalculator myCalculator = Calculators.ToList<ICalculator>()[1];
- var operations = myCalculator.GetOperations();
- var operationsDict = new SortedList<string, IOperation>();
- foreach(IOperation item in operations)
- {
- Console.WriteLine("Name:{0},number operands:{1}"
- , item.Name, item.NumberOperands);
- operationsDict.Add(item.Name, item);
- }
- Console.WriteLine();
- string selectedOp = null;
- do
- {
- try
- {
- Console.Write("Operation?");
- selectedOp = Console.ReadLine();
- if (selectedOp.ToLower() == "exit"
- || !operationsDict.ContainsKey(selectedOp))
- {
- continue;
- }
- var operation = operationsDict[selectedOp];
- double[] operands = new double[operation.NumberOperands];
- for (int i = 0; i < operation.NumberOperands; i++)
- {
- Console.WriteLine("\t operand {0}?", i + 1);
- string selectedOperand = Console.ReadLine();
- operands[i] = double.Parse(selectedOperand);
- }
- Console.WriteLine("calling calculator");
- double result = myCalculator.Operate(operation, operands);
- Console.WriteLine("result:{0}", result);
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- Console.WriteLine();
- continue;
- }
- } while (selectedOp != "exit");
- }
- public void Run2()
- {
- var catalog = new DirectoryCatalog("c:\\plugins");
- var container = new CompositionContainer(catalog);
- container.ComposeParts(this);
- Bomb.Invoke();
- Console.ReadKey();
- }
- }
- }
分析:
标记“[ImportMany(typeof(ICalculator))]”,该声明表达的意图是:将所有声明了标记“[Export(typeof(ICalculator))]”的程序集加载进容器。这里“[ImportMany]和”[Import]”的区别就是:前者的容器可以存放多个插件,而后者只能存放一个。
光声明“[Import()]”和”[Export()]”标记是不行的,还必须通过下面的代码将这两个标记的功能联合起来:
- //DirectoryCatalog表示这类插件会存放在系统的哪个文件夹下
- var catalog = new DirectoryCatalog("c:\\plugins");
- var container = new CompositionContainer(catalog);
- try
- {
- //将存放在目录中的插件按“[Export()]和[Import()]”规则装载进当前
- //类中。
- container.ComposeParts(this);
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- return;
- }
执行结果
Name:+,number operands:2 Name:-,number operands:2 Name:*,number operands:2 Name:/,number operands:2 Operation?+ operand 1? 1 operand 2? 1 calling calculator result:2 Operation?exit you are dead!!! |
MAF(Managed Addin Framework)
MAF也是.Net为我们提供的一个“插件编程”解决方案。它比MEF复杂,需要配置很多元素。但它也有些优点:1.宿主程序和插件程序可以进行隔离,以此降低运行插件所带来的风险;2。MAF的设计是基于7个程序集组成的管道,这些管道部分可以单独更换,这些管道的详细情况见下图。
图1
使用MAF是需要有些细节需要注意:组成管道的7个程序集在系统中的保存路径有格式要求,并且没个保存它的文件夹内只运行同时出现一个程序集。具体情况如下图所示:
图2
图3
图4
图5
下面我们来看一个小Demo吧,这个demo一共有7个项目,它们分别对应图1描述的管道中的7个部分。具体情况见下图。
图6
插件:Addin_1,Addin_2 插件视图:AddinSideView 插件适配器:AddinSideAdapter 协定:IContract 宿主视图:HostSideView 宿主适配器:HostSideAdapter 宿主程序:Host |
程序代码
Addin_1
- /*
- 作者:GhostBear
- 博客:http://blog.csdn.net/ghostbear
- 简介:测试MAF,这段代码是用来定义一个插件的。这个插件可以在宿主程序
- 中动态加载。
- */
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.AddIn;
- using System.AddIn.Pipeline;
- namespace Addin_1
- {
- [AddIn("Helloworld",Description="this is helloworld addin"
- ,Publisher="GhostBear",Version="1.0")]
- public class Addin_1:AddinSideView.AddinSideView
- {
- public string Say()
- {
- return "Helloworld";
- }
- }
- }
Addin_2
- /*
- 作者:GhostBear
- 博客:http://blog.csdn.net/ghostbear
- */
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.AddIn;
- namespace Addin_2
- {
- [AddIn("SuperBomb",Description="This is a bigger bomb"
- ,Publisher="SuperWorker",Version="1.0.0.0")]
- public class Addin_2:AddinSideView.AddinSideView
- {
- public string Say()
- {
- return "B--O--M--B";
- }
- }
- }
AddinSideView
- /*
- 作者:GhostBear
- 博客:http://blog.csdn.net/ghostbear
- * 简介:测试MAF,这段代码是定义插件端的视图类,该视图类的方法和属性必须与协定一致。
- */
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.AddIn.Pipeline;
- namespace AddinSideView
- {
- [AddInBase()]
- public interface AddinSideView
- {
- string Say();
- }
- }
AddinSideAdapter
- /*
- 作者:GhostBear
- 博客:http://blog.csdn.net/ghostbear
- * 简介:测试MAF,这段代码是插件端的适配器类,它用来实现插件端视图类。
- * 并组合协定。这样就能让插件和协定解耦,如果插件有所修改就换掉
- * 该适配器类就可以了。
- */
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.AddIn.Pipeline;
- namespace AddinSideAdapter
- {
- [AddInAdapter]
- public class AddinSideAdapter : ContractBase,IContract.IMyContract
- {
- private AddinSideView.AddinSideView _handler;
- public AddinSideAdapter(AddinSideView.AddinSideView handler)
- {
- this._handler = handler;
- }
- public string Say()
- {
- return this._handler.Say();
- }
- }
- }
IContract
- /*
- 作者:GhostBear
- 博客:http://blog.csdn.net/ghostbear
- 简介:测试MAF,这段代码是定义协定。
- */
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.AddIn.Pipeline;
- using System.AddIn.Contract;
- namespace IContract
- {
- [AddInContract]
- public interface IMyContract:System.AddIn.Contract.IContract
- {
- string Say();
- }
- }
HostSideView
- /*
- 作者:GhostBear
- 博客:http://blog.csdn.net/ghostbear
- 简介:测试MAF,这段代码用来定义宿主段的视图类,该类的所有方法和属性需与协定类一致。
- */
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace HostSideView
- {
- public interface HostSideView
- {
- string Say();
- }
- }
HostSideAdapter
- /*
- 作者:GhostBear
- 博客:http://blog.csdn.net/ghostbear
- 简介:测试MAF,这段代码用来定义宿主端的适配器类。该类实现宿主端的
- 视图类并组合协定。
- */
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.AddIn.Pipeline;
- namespace HostSideAdapter
- {
- [HostAdapter()]
- public class HostSideAdapter:HostSideView.HostSideView
- {
- private IContract.IMyContract _contract;
- //这行代码重要
- private System.AddIn.Pipeline.ContractHandle _handle;
- public HostSideAdapter(IContract.IMyContract contract)
- {
- this._contract = contract;
- this._handle = new ContractHandle(contract);
- }
- public string Say()
- {
- return this._contract.Say();
- }
- }
- }
Host
- /*
- 作者:GhostBear
- 博客:http://blog.csdn.net/ghostbear
- 简介:测试MAF,这段代码是宿主程序。该程序可以针对保存在某个目录下的插件来进行选择性调用。
- */
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Collections;
- using System.Collections.ObjectModel;
- using System.AddIn.Hosting;
- using HostSideView;
- namespace Host
- {
- class Program
- {
- static void Main(string[] args)
- {
- string path = @"D:\学习文档\c#\c#高级编程7\MAF\MAF";
- string[] warnings = AddInStore.Update(path);
- foreach (var tmp in warnings)
- {
- Console.WriteLine(tmp);
- }
- //发现
- var tokens = AddInStore.FindAddIns(typeof(HostSideView.HostSideView), path);
- Console.WriteLine("当前共有{0}个插件可以选择。它们分别为:",tokens.Count);
- var index = 1;
- foreach (var tmp in tokens)
- {
- Console.WriteLine(string.Format("[{4}]名称:{0},描述:{1},版本:{2},发布者:{3}", tmp.Name, tmp.Description, tmp.Version, tmp.Publisher,index++));
- }
- var token = ChooseCalculator(tokens);
- //隔离和激活插件
- AddInProcess process=new AddInProcess(Platform.X64);
- process.Start();
- var addin = token.Activate<HostSideView.HostSideView>(process, AddInSecurityLevel.FullTrust);
- Console.WriteLine("PID:{0}",process.ProcessId);
- //调用插件
- Console.WriteLine(addin.Say());
- Console.ReadKey();
- }
- private static AddInToken ChooseCalculator(Collection<AddInToken> tokens)
- {
- if (tokens.Count == 0)
- {
- Console.WriteLine("No calculators are available");
- return null;
- }
- Console.WriteLine("Available Calculators: ");
- // Show the token properties for each token in the AddInToken collection
- // (tokens), preceded by the add-in number in [] brackets.
- int tokNumber = 1;
- foreach (AddInToken tok in tokens)
- {
- Console.WriteLine(String.Format("\t[{0}]: {1} - {2}\n\t{3}\n\t\t {4}\n\t\t {5} - {6}",
- tokNumber.ToString(),
- tok.Name,
- tok.AddInFullName,
- tok.AssemblyName,
- tok.Description,
- tok.Version,
- tok.Publisher));
- tokNumber++;
- }
- Console.WriteLine("Which calculator do you want to use?");
- String line = Console.ReadLine();
- int selection;
- if (Int32.TryParse(line, out selection))
- {
- if (selection <= tokens.Count)
- {
- return tokens[selection - 1];
- }
- }
- Console.WriteLine("Invalid selection: {0}. Please choose again.", line);
- return ChooseCalculator(tokens);
- }
- }
- }
分析
在上面的7个程序集,起解耦作用的关键还是2个适配器类。调用程序不直接调用协定,而是通过通过调用这2个适配器来间接调用协定。
小结
MEF和MAF为我们实现“插件编程”提供了2中选择,它们设计的出发点也是完全不同的。在使用它们的时候还是需要更加具体需求来权衡使用。
.Net插件编程模型:MEF和MAF[转载]的更多相关文章
- jQuery插件编写及链式编程模型小结
JQuery极大的提高了我们编写JavaScript的效率,让我们可以愉快的编写代码,做出各种特效.大多数情况下,我们都是使用别人开发的JQuery插件,今天我们就来看看如何把我们常用的功能做出JQu ...
- jQuery插件编写及链式编程模型
jQuery插件编写及链式编程模型小结 JQuery极大的提高了我们编写JavaScript的效率,让我们可以愉快的编写代码,做出各种特效.大多数情况下,我们都是使用别人开发的JQuery插件,今天我 ...
- 转载:Spark中文指南(入门篇)-Spark编程模型(一)
原文:https://www.cnblogs.com/miqi1992/p/5621268.html 前言 本章将对Spark做一个简单的介绍,更多教程请参考:Spark教程 本章知识点概括 Apac ...
- 【.Net平台下插件开发】-MEF与MAF初步调研
背景 Team希望开发一个插件的平台去让某搜索引擎变得更好.主要用于采集一些不满意信息(DSAT)给Dev.这些信息会由不同的team提供不同的 tool分析.有的提供仅仅是一个website,有 ...
- Scala进阶之路-并发编程模型Akka入门篇
Scala进阶之路-并发编程模型Akka入门篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Akka Actor介绍 1>.Akka介绍 写并发程序很难.程序员不得不处 ...
- JS魔法堂:深究JS异步编程模型
前言 上周5在公司作了关于JS异步编程模型的技术分享,可能是内容太干的缘故吧,最后从大家的表情看出"这条粉肠到底在说啥?"的结果:(下面是PPT的讲义,具体的PPT和示例代码在h ...
- 谈谈c#中异步编程模型的变迁
大家在编程过程中都会用到一些异步编程的情况.在c#的BCL中,很多api都提供了异步方法,初学者可能对各种不同异步方法的使用感到迷惑,本文主要为大家梳理一下异步方法的变迁以及如何使用异步方法. Beg ...
- Spark入门实战系列--3.Spark编程模型(下)--IDEA搭建及实战
[注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 . 安装IntelliJ IDEA IDEA 全称 IntelliJ IDEA,是java语 ...
- Atitit.web三大编程模型 Web Page Web Forms 和 MVC
Atitit.web三大编程模型 Web Page Web Forms 和 MVC 1. 编程模型是 Web Forms 和 MVC (Model, View, Controller). 2. ...
随机推荐
- springMVC+mybatis用户登录实例
1.整体结构 2.准备工作 数据库: --Mysql 5.6 创建数据库 wolf 1 CREATE DATABASE wolf; 创建用户表 user 1 2 3 4 5 6 create tabl ...
- XTU OJ 1209 Alice and Bob 2014(嘉杰信息杯ACM/ICPC湖南程序设计邀请赛暨第六届湘潭市程序设计竞赛)
Problem Description The famous "Alice and Bob" are playing a game again. So now comes the ...
- 涂抹Oracle笔记2:数据库的连接-启动-关闭
一.数据库的连接sqlplus <username>[/<password>][@<connect_idertifier>]|/[as sysdba| as sys ...
- Win7刷新环境变量
在“我的电脑”->“属性”->“高级”->“环境变量”中增加或修改环境变量后,需重启系统才能使之生效.有没有什么方法可让它即时生效呢? 下面介绍一种方法: 以修改环境变量“PATH” ...
- mysql 5.7占用400M内存优化方案
mysql出问题了,装了一下新版本,竟然占用400多M的内存,这对于不是服务器,占用是在太高了,再开打一个开发工具,那电脑很卡了,其实是可以优化一下的,在my.ini文件找到这几个参数更改一下,占用大 ...
- [C++程序设计]基于对象的程序设计 基于对象的程序设计
1. 面向对象分析(object oriented analysis,OOA)2. 面向对象设计(object oriented design,OOD)3. 面向对象编程(object oriente ...
- scp和pscp
在linux中,我们常用scp命令传输文件: 如以下实例,我们想把当前服务器文件abc.sql传输到192.168.1.1服务器上,我们可以执行以下命令: scp /home/person/hww/a ...
- js中得call()方法和apply()方法的用法
方法定义 call方法: 语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]]) 定义:调用一个对象的一个方法,以另一个对象替换当前对象. 说明: call 方 ...
- centos 6.5 安装 redis
下载软件: wget wget http://download.redis.io/releases/redis-2.8.7.tar.gz 2.解压软件并编译安装: tar -zxvf redis-2. ...
- 【转】WPF - 第三方控件
WPF - 第三方控件 目前第三方控件在网上形成巨大的共享资源,其中包括收费的也有免费的,有开源的也有不开源的,合理的使用第三方控件将使项目组的工作事半功倍.比如项目中有些复杂的业务逻辑.有些绚丽的效 ...