C# 依赖注入 MEF

article/2025/9/15 1:23:36

1、什么是MEF

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

也有人把MEF解释为“依赖注入”的一种方式,那么什么是“依赖注入”?,了解MEF只需要抓住以下几个关键点:

(1)字面意思,可扩展的framework,或者叫可扩展的库。也就是说,使用MEF是为了提高程序的可扩展性。MEF会根据指定的导入导出自动去发现匹配的扩展,不需要进行复杂的程序配置。

(2)在设计层面上来说,为什么要使用MEF?为了“松耦合”!我们知道,程序设计有几个原则,“高内聚,低耦合”就是其中一个。使用MEF可以帮助我们减少内库之间的耦合。 

 

2、为什么要使用MEF:上面已经解释过,为了程序的扩展和“松耦合”。

 

3、MEF的使用:

(1)MEF基础导入导出的使用:

MEF的使用步骤主要分三步:宿主MEF并组合部件、标记对象的导出、对象的导入使用。

我们先来看一个Demo。

   class Program2{//导入对象使用[Import("chinese_hello")]public Person oPerson { set; get; }static void Main(string[] args){var oProgram = new Program2();oProgram.MyComposePart();var strRes = oProgram.oPerson.SayHello("李磊");Console.WriteLine(strRes);Console.Read();}//宿主MEF并组合部件void MyComposePart(){var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());var container = new CompositionContainer(catalog);//将部件(part)和宿主程序添加到组合容器container.ComposeParts(this);}}public interface Person{string SayHello(string name);}//声明对象可以导出[Export("chinese_hello", typeof(Person))]public class Chinese : Person{public string SayHello(string name){return "你好:" + name ;}}[Export("american_hello", typeof(Person))]public class American : Person{public string SayHello(string name){return "Hello:" + name ;}}

复制代码

得到结果:

我们来分析下这段代码:

     //宿主MEF并组合部件 void MyComposePart() { var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());

var container = new CompositionContainer(catalog); //将部件(part)和宿主程序添加到组合容器 container.ComposeParts(this); }

复制代码

这个方法表示添加当前Program2这个类到组合容器,为什么要添加到组合容器?是因为只要添加到组合容器中之后,如果该类里面有Import,MEF才会自动去寻找对应的Export。这也就是为什么使用MEF前必须要组合部件的原因。

复制代码

    [Export("chinese_hello", typeof(Person))]public class Chinese : Person{public string SayHello(string name){return "你好:" + name ;}}

复制代码

这里的[Export("chinese_hello", typeof(Person))]这个特性表示标记Chinese类的导出。

将Export转到定义可以看到:

复制代码

        //// 摘要: //     通过在指定协定名称下导出指定类型,初始化 System.ComponentModel.Composition.ExportAttribute 类的新实例。//// 参数: //   contractName://     用于导出使用此特性标记的类型或成员的协定名称,或 null 或空字符串 ("") 以使用默认协定名称。////   contractType://     要导出的类型。public ExportAttribute(string contractName, Type contractType);

复制代码

这里的两个参数:第一个表示协定名称,如果找到名称相同的Import,那么就对应当前的Chinese对象;第二个参数表示要导出的类型。

[Import("chinese_hello")]public Person oPerson { set; get; }

这里的chinese_hello是和Export里面的chinese_hello对应的,由此可知,每一个[Import("chinese_hello")]这种Import一定可以找到一个对应的Export,如果找不到,程序就会报异常。当然如果这里的Import如果改成[Import("american_hello")],那么oPerson肯定就对应一个American对象。

通过上面的程序可以知道,我们使用[Import]这个特性,它的底层其实就是给我们初始化了一个对象。例如上面的[Import("chinese_hello")]等价于Person oPerson=new Chinese();。看到这里可能有人就会说这个Import是多此一举了,既然我们可以new,为什么非要用这种奇怪的语法呢,怪别扭的。其实如果我们站在架构的层面,它的好处就是可以减少dll之间的引用。这个留在下一篇来讲。

 

(2)MEF导入导出扩展:

按照MEF的约定,任何一个类或者是接口的实现都可以通过[System.ComponentModel.Composition.Export] 属性将其他定义组合部件(Composable Parts),在任何需要导入组合部件的地方都可以通过在特定的组合部件对象属性上使用[System.ComponentModel.Composition.Import ]实现部件的组合,两者之间通过契约(Contracts)进行通信。通过上面的例子我们可以知道,对象是可以通过Import和Export来实现导入和导出的,那么我们进一步扩展,对象的属性、字段、方法、事件等是否也可以通过[ImportAttribute]进行导入呢?

复制代码

   class Program2{[Import("TestProperty")]public string ConsoleTest { get; set; }static void Main(string[] args){var oProgram = new Program2();oProgram.MyComposePart();Console.WriteLine(oProgram.ConsoleTest);Console.Read();}void MyComposePart(){var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());var container = new CompositionContainer(catalog);//将部件(part)和宿主程序添加到组合容器container.ComposeParts(this);}}public class TestPropertyImport{[Export("TestProperty")]public string TestMmport { get { return "测试属性可以导入导出"; } }}

复制代码

得到结果:

由此说明,属性也是可以导入导出的。原理与上类似。既然属性可以,那么字段就不用演示了,它和属性应该是类似的。

下面来看看方法是否可以呢?

复制代码

class Program2{[Import("chinese_hello")]public Person oPerson { set; get; }[Import("TestProperty")]public string ConsoleTest { get; set; }[Import("helloname")]public Action<string> TestFuncImport { set; get; }static void Main(string[] args){var oProgram = new Program2();oProgram.MyComposePart();oProgram.TestFuncImport("Jim");//Console.WriteLine(oProgram.ConsoleTest);//var strRes = oProgram.oPerson.SayHello("李磊");//Console.WriteLine(strRes);Console.Read();}void MyComposePart(){var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());var container = new CompositionContainer(catalog);//将部件(part)和宿主程序添加到组合容器container.ComposeParts(this);}}public class TestPropertyImport{[Export("TestProperty")]public string TestMmport { get { return "测试属性可以导入导出"; } }[Export("helloname", typeof(Action<string>))]public void GetHelloName(string name){Console.WriteLine("Hello:" + name);}}

复制代码

由此可知,方法的导入和导出是通过匿名委托的方式实现的,那么由此类推,事件应该也是可以的,有兴趣的朋友可以一试。原理和上面是一样一样的。

既然属性、字段、方法、事件都可以通过Import和Export实现单一对象或变量的导入和导出,那么如果我们想要一次导入多个对象呢?嘿嘿,微软总是体贴的,它什么都为我们考虑到了。我们来看看如何实现。

 

复制代码

class Program2{[ImportMany]public IEnumerable<Person> lstPerson { set; get; }static void Main(string[] args){var oProgram = new Program2();oProgram.MyComposePart();Console.WriteLine(oProgram.lstPerson.Count());Console.Read();}void MyComposePart(){var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());var container = new CompositionContainer(catalog);//将部件(part)和宿主程序添加到组合容器container.ComposeParts(this);}}public interface Person{string SayHello(string name);}[Export(typeof(Person))]public class Chinese : Person{public string SayHello(string name){return "你好:" + name ;}}[Export(typeof(Person))]public class American : Person{public string SayHello(string name){return "Hello:" + name ;}}

复制代码

得到的结果为2。这里有一点需要注意的,使用ImportMany的时候对应的Export不能有chinese_hello这类string参数,否则lstPerson的Count()为0.

 

(3)MEF的延迟加载

我们知道,当装配一个组件的时候,当前组件里面的所有的Import的变量都自动去找到对应的Export而执行了实例化,有些时候,出于程序效率的考虑,不需要立即实例化对象,而是在使用的时候才对它进行实例化。MEF里面也有这种延迟加载的机制。

复制代码

class Program2{[Import("chinese_hello")]public Person oPerson { set; get; }[Import("american_hello")]public Lazy<Person> oPerson2 { set; get; }static void Main(string[] args){var oProgram = new Program2();oProgram.MyComposePart();var strRes = oProgram.oPerson.SayHello("李磊");var strRes2 = oProgram.oPerson2.Value.SayHello("Lilei");Console.WriteLine(strRes);Console.Read();}void MyComposePart(){var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());var container = new CompositionContainer(catalog);//将部件(part)和宿主程序添加到组合容器container.ComposeParts(this);}}public interface Person{string SayHello(string name);}[Export("chinese_hello", typeof(Person))]public class Chinese : Person{public string SayHello(string name){return "你好:" + name ;}}[Export("american_hello", typeof(Person))]public class American : Person{public string SayHello(string name){return "Hello:" + name ;}}

复制代码

通过调试可知,当程序运行到var strRes = oProgram.oPerson.SayHello("李磊");这一行的时候

oPerson对象已经实例化了,而oPerson2.Value对象没有实例化,当程序执行var strRes2 = oProgram.oPerson2.Value.SayHello("Lilei")这一句的时候,oPerson2.Value对象才进行实例化。这种需要在某些对程序性能有特殊要求的情况下面有一定的作用。

 

讲到这里,我们再来看前面关于理解MEF的两个关键点:

(1)可扩展的库:由于MEF允许通过Import的方式直接导入对象、属性、方法等,试想,有人开发了一个组件,他们事先定义好了一系列的导出(Export),我们只需要将它的组件引进来,使用Import的方式按照他们Export的约定导入对象即可,不用做其他复杂的配置。

(2)能更好的实现“松耦合”:比如我们项目按照面向接口编程的方式这样分层:UI层、BLL接口层、BLL实现层......UI层只需要引用BLL接口层即可,我们在BLL实现层里面定义好Export的导出规则,然后再UI层里面使用Import导入BLL实现层的对象即可,这样UI层就不需要添加BLL实现层的引用。减少了dll之间的依赖。


http://chatgpt.dhexx.cn/article/lM2kCanI.shtml

相关文章

MEF 基础简介 一

前言 小编菜鸟级别的程序员最近感慨颇多&#xff0c;经历了三五春秋深知程序路途遥远而我沧海一粟看不到的尽头到不了的终点何处是我停留的驿站。说了段废话下面进入正题吧&#xff01; 什么是MEF&#xff1f; MEF&#xff1a;全称Managed Extensibility Framework&#xff08;…

MEF插件式开发

一、MEF 简介 1、 Managed Extensibility Framework简称MEF&#xff0c;是WPF中的一种插件式开发的框架。其开发程序的主要优点如下&#xff1a; 易于将复杂程序进行拆分成不同的功能模块&#xff0c;然后进行多团队的协作。将各个功能编译成dll的形式提供给主程序&#xff0…

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

在文章开始之前&#xff0c;首先简单介绍一下什么是MEF&#xff0c;MEF,全称Managed Extensibility Framework&#xff08;托管可扩展框架&#xff09;。单从名字我们不难发现&#xff1a;MEF是专门致力于解决扩展性问题的框架&#xff0c;MSDN中对MEF有这样一段说明&#xff1…

【C#进阶系列】【MEF框架(一)】

C#进阶系列 第一章 【C#进阶系列】【MEF框架&#xff08;一&#xff09;】 文章目录 C#进阶系列前言一、MEF介绍二、为什么要用MEF三、MEF的概念四、使用示例五、MEF框架的好处六、源码链接总结 前言 这里对MEF作了基本的介绍&#xff0c;包括使用了一个特定场景&#xff08;…

【虚拟化生态平台】平台架构图思路和实现细节

设计思路 我需要一个内网生态&#xff0c;通过一个nginx访问到我所有的资源&#xff0c;内部实现细节我不关心&#xff0c;那么就需要一个nginx服务器来负载所有的服务服务器太多了&#xff0c;我不想一个个的记录ip去登录&#xff0c;那我就需要一个跳板机来记录我的服务器&a…

ARM平台的虚拟化介绍

本篇博文主要介绍虚拟化的基本思想以及在arm平台如何做虚拟化&#xff0c;arm提供的硬件feature等等。 虚拟化技术简介 虚拟化技术 虚拟化是一个概念&#xff0c;单从这个概念的角度来看&#xff0c;只要是用某一种物品去模拟另一种物品都可以称为虚拟化&#xff0c;甚至于有…

ARMv8虚拟化

1 概述 2 虚拟化简介 3 AArch64虚拟化 4 Stage-2地址转换 5 指令的陷入和模拟 6 虚拟化异常 7 虚拟化通用定时器 8 虚拟化主机扩展 9 嵌套虚拟化 10 安全空间的虚拟化 11 虚拟化的成本 12 小测验 13 其它参考文章 14 接下来的计划 1 概述 本文描述了ARMv8-64的虚…

虚拟化服务器环境搭建方案,虚拟化环境建设方案

虚拟化环境建设方案 市场背景: 随着IT技术的飞速发展,基础传统IT架构模式下的应用规模扩大,需要更多的服务器来支持业务的发展,同时出现了如下几个问题: 1.资源利用率低、闲置率高,运行效率低 2.应急处理响应时间慢、服务保障差 3.运行维护成本高 虚拟化的出现很好的解决…

搭建vmware虚拟化平台的基础配置,以及Hadoop平台的搭建

需要准备的东西&#xff1a; vmware workstations centos.iso hadoop3.3.0 mobaxterm/xshell/pietty/winscp jdk的tar包 第一步 &#xff1a; 安装centos操作系统 第二步 &#xff1a; 克隆虚拟机&#xff08;也可以在hadoop安装后再克隆&#xff09; 我们这里需要选择完整克隆…

WSL2 请启用虚拟机平台 Windows 功能并确保在 BIOS 中启用虚拟化

bcdedit /set hypervisorlaunchtype autoC:\WINDOWS\system32>bcdedit /set hypervisorlaunchtype auto 操作成功完成。

嵌入式虚拟化平台jailhouse踩坑笔记

目录 简介 相关论文 架构 jailhouse的应用示例 jailhouse 官方demo jailhouse的运行 jailhouse none-root cell中挂载nfs作为根目录启动linux 注意事项 jailhouse的配置 1.cell配置文件简介 2. cell文件的生成过程 2. .c配置文件内容解析 QEMU中实现jailhouse的…

搭建华为FusionCompute虚拟化平台

最小实验要求&#xff1a;两台服务器 1、通过BMC口安装CNA操作系统 远程光驱安装CNA&#xff0c;需要设置服务器从远程光驱启动&#xff0c;然后进入BIOS可以查看BMC控制台的用户名和密码&#xff0c;以及IP&#xff0c;一般服务器都默认有。然后登陆BMC的控制台&#xff0c;进…

服务器虚拟化 涉密,面向高安全服务器虚拟化领域应用研制的安全保密虚拟化平台...

中标麒麟服务器虚拟化系统是基于中标麒麟虚拟化平台软件V7&#xff0c;面向高安全服务器虚拟化领域应用研制的安全保密虚拟化平台&#xff1b;产品通过国家保密科技测评中心基于《涉密信息系统服务器虚拟化和桌面虚拟化产品安全保密技术要求》(BMB30-2017)的安全性检测。支持海…

VA虚拟平台十大亮点

VA虚拟平台十大亮点 一、自适应多种安装环境、并可以手动调整二、兼容互联网上主流动态域名自动连接&#xff0c;并有自主的动态域名解析。三、系统稳定性有效保障和看门狗机制四、高级参数最大限度保障程序兼容性五、智能打印-动态调整、实时预览、多页排版、自动打印六、向下…

Linux虚拟化平台检测

如果要找出 Linux 系统运行在虚拟化平台中还是硬件服务器上&#xff0c;有多种方式可供大家选择&#xff0c;这主要取决于你的 hypervisor 或 container 环境。不同的虚拟化或容器技术会在其实例中引入不同的识别指纹&#xff0c;如&#xff1a;处理器厂商、特殊的 /proc 文件或…

虚拟化平台PVE(ProxmoxVirtual Environment)安装部署

PVE是一个完整的企业虚拟化开源平台。通过内置的web界面&#xff0c;可以轻松地管理虚拟机和容器、软件定义的存储和网络、高可用性集群和多个开箱即用的工具。 官方&#xff1a;Proxmox - Powerful open-source server solutions 部署前准备 1.下载镜像&#xff0c;并刻录到U…

云平台管理与部署---虚拟化平台-----KVM

第一&#xff0c;搭建KVM服务器 1&#xff0c;虚拟化平台的安装 # yum -y install qemu-kvm libvirt-daemon libvirt-client libvirt-daemon-driver-qemu # systemctl start libvirtd # ls /etc/libvirt/qemu # ls /var/lib/libvirt/images/ 第二&#xff0c;管理KVM平台 1…

云计算-华为虚拟化平台FusionCompute

目录 FusionCompute产品介绍 FusionCompute虚拟化套件 FusionCompute产品定位 FusionCompute在虚拟化套件中位置 FusionCompute产品架构 FusionCompute模块功能模块 ​编辑FusionCompute产品功能 - 虚拟化计算 FusionCompute产品功能 - 虚拟化存储 FusionCompute产品功…

服务器虚拟化如何分配内存,VMware虚拟化平台内存如何管理?

当运行一个虚拟机时,vSphere的VMKernel为虚拟机生成一段可编址的连续内存,与普通操作系统提供给上层应用使用的内存具有相同的属性特征。引入内存虚拟化之后,同样的内存地址空间,允许VMkernel同时运行多个虚拟机并保证它们之间使用内存的独立性。 虚拟化平台三种内存模式 主…

Linux部署KVM虚拟化平台

了解KVM&#xff1a; Kernel-based Virtual Machine的简称&#xff0c;是一个开源的系统虚拟化模块&#xff0c;自Linux 2.6.20之后集成在Linux的各个主要发行版本中。它使用Linux自身的调度器进行管理&#xff0c;所以相对于Xen&#xff0c;其核心源码很少。KVM已成为…