1.概要
什么是MEF?
Managed Extensibility Framework (MEF) 是用于创建可扩展的轻量级应用程序的库。 它让应用程序开发人员得以发现和使用扩展且无需配置。 它还让扩展开发人员得以轻松地封装代码并避免脆弱的紧密依赖性。 MEF 让扩展不仅可在应用程序内重复使用,还可以跨程序重复使用。
如果聊到MEF不得不提到的是IoC(IoC-Invertion of Control),即控制反转。它是一种程序设计指导思想。而MEF、Unity、MAF(包括Prism框架部分)、Ninject、StructureMap、Autofac等,都是IoC思想的具体实现。
IoC中的概念
(1)依赖(Dependency):表示有关联,一个类依赖于另一个类。在日常编码的时候大家追求的都是高内聚低耦合这种就是良性的依赖。而常说的x山代码牵一发动全身的则是恶性依赖重则推到重构、轻则维护困难。
(2)依赖倒置原则(DIP):设计模式六大原则之一,是一种软件架构设计原则。高层模块不应依赖于底层模块,两者应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
(3)控制反转(IoC):一种软件设计原则,上层对下层的依赖(即底层模块的获得)交给第三方。例如在WPF里常用的MVVM模式也会涉及到这样的概念,在ViewModel层想调用View的窗体关闭这个时候VM层没有办法直接调用,VM层又不知道自己什么时候会被初始化(View层)。这个时候就可以用控制反转的思维,让View层决定什么时候初始化ViewModel再将关闭View的权利转交给ViewModel。
(4)依赖注入(DI):实现IoC的一种方式、手段。将标记了Export的特性的dll注册到IoC容器中。
(5)IoC容器:
依赖注入的框架,用来映射依赖,管理对象的创建和生存周期。
- 动态创建、注入依赖对象;
- 管理对象生命周期(Singleton:单例全剧唯一实例、Scoped:作用域,在一个作用域中唯一实例、Transient:每次调用的实例都是新对象);
- 映射依赖关系;
(6)特性
- 特性的任务:特性就是为了支持对象添加一些自我描述的信息,不影响类封装的前提添加额外信息。如果你用这个信息,那特性就有用;如果你不需要这个信息,那么这个特性就没用。
- 特性的基类:Attribute。例如:Obsolete特性,提出警告信息或错误信息,特性可以影响编译、影响运行。
- 特性类通常用Attribute结尾,在使用的时候可以用全称,也可以去掉这个结尾,也可以加上小括号显示调用构造函数,如果不加小括号默认调用无参构造函数,也可以在括号内直接给属性或字段赋值。
- 特性往往只能修饰一个对象一次,需要设置属性的属性的时候,需要给属性添加AttributeUsage属性,可以用来设置:是否允许多次修饰、修饰对象的类别(类or字段等)
- DLL文件=IL中间语言 metadata元数据,特性信息会被编译到元数据中。
- 使用场景:为类或成员添加描述信息,然后在使用的时候拿到该信息
(7)MEF提供三种方式发现部件
- AssemblyCatalog 在当前程序集发现部件。
- DirectoryCatalog 在指定的目录发现部件。
- DeploymentCatalog 在指定的XAP文件中发现插件。
(8)插件
(图片来自网络)
MEF设计思维是插件化编程,那么插件怎么实现呢?插件简单来说可以理解为一个类库,这个类库一般会作为一个业务模块进行划分。这里就需要提到一个叫做特性的概念,上图中Export和Import就是特性。特性在这里的主要作用帮助Catalog发现基于约定特殊实现条件编码的插件。
2.详细内容
源码地址:https://github.com/JusterZhu/Further-learning-WPF/tree/main/P8MEF
IoC(MEF)容器
代码语言:javascript复制using Chapter8.Infrastructure.Common.Interfaces;
using Chapter8.Infrastructure.Common.MVVM;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
namespace Chapter8.ViewModels
{
public class MainViewModel : BindableBase
{
/// <summary>
/// 插件集合
/// </summary>
[ImportMany]
public List<IPlug> Plugs { get; set; }
private CompositionContainer _container;
private ObservableCollection<IPlug> _appPlugs;
private IPlug _currentPlugs;
private UserControl _view;
public ObservableCollection<IPlug> AppPlugs
{
get { return _appPlugs; }
set
{
_appPlugs = value;
OnPropertyChanged("AppPlugs");
}
}
public IPlug CurrentPlug
{
get { return _currentPlugs; }
set
{
_currentPlugs = value;
OnPropertyChanged("CurrentPlug");
}
}
public MainViewModel()
{
Composes();
AppPlugs = new ObservableCollection<IPlug>(Plugs);
CurrentPlug = AppPlugs.FirstOrDefault();
}
public void Composes()
{
var dir = AppDomain.CurrentDomain.BaseDirectory;
//初始化该目录下所有复合条件的DLL对象
var catalog = new DirectoryCatalog(dir);
//将所有DLL对象进行组装
_container = new CompositionContainer(catalog);
//指定最终组装承载的容器对象
_container.ComposeParts(this);
}
}
}
插件
代码语言:javascript复制using Chapter8.AddFriend.Views;
using Chapter8.Infrastructure.Common.Interfaces;
using System;
using System.ComponentModel.Composition;
namespace Chapter8.AddFriend
{
[Export(typeof(IPlug))]
public class AddFriendPlug : IPlug
{
private AddFriendView _view;
/// <summary>
/// 模块名称
/// </summary>
public string Name { get => "AddFriendPlug"; }
/// <summary>
/// 模块唯一标识
/// </summary>
public string UUID { get => "11EDEF73-6D73-42F7-82F8-EE39BBF336FD"; }
public IView View { get => _view ?? (_view = new AddFriendView()); }
public void Init()
{
//该业务模块插件初始化的时候需要做的事情,例如加载资源文件
}
public void Release()
{
//释放该业务模块插件的资源
}
}
}
3.Ref
https://learn.microsoft.com/zh-cn/dotnet/framework/mef/
https://www.cnblogs.com/atomy/p/12516304.html