MEF官方解释 : MEF(Managed Extensibility Framework)是一个用于创建可扩展的轻型应用程序的库。 应用程序开发人员可利用该库发现并使用扩展,而无需进行配置。 扩展开发人员还可以利用该库轻松地封装代码,避免生成脆弱的硬依赖项。 通过 MEF,不仅可以在应用程序内重用扩展,还可以在应用程序之间重用扩展。
作用: (1) 使用MEF是为了提高程序的可扩展性。MEF会根据指定的导入导出自动去发现匹配的扩展,获取到相应类型的对象,不需要进行复杂的程序配置
(2) 程序设计有几个原则,“高内聚,低耦合”就是其中一个。使用MEF可以帮助我们减少内库之间的耦合。
NuGet安装MEF2, 包名是 Microsoft.Composition,适用于 .NET Framework 4.5 及以上,.NET Core 和各种 .NET 移动平台
使用说明:添加引用System.Composition,
MEF 完全使用特性来管理容器中的依赖,Import/Export:在类型上标记 [Export]
可以让容器发现这个类型。[Export]
允许带两个参数,一个契约名称,一个契约类型。在 [Import]
的时候,相同的契约名称会被注入
示例:
1. 简单使用
类ModelA实现接口IModel,ModelA添加特性[Export(typeof(IModel))],创建Container容器管理依赖
namespace MEF.Interface
{
public interface IModel
{
void Do();
}
}
namespace MEF.ViewModels
{
[Export(typeof(IModel))]
public class ModelA : IModel
{
public void Do()
{
Console.WriteLine(MethodBase.GetCurrentMethod().DeclaringType.FullName);
}
}
}
public class Container
{
private static Container _instance;
public static Container Instance
{
get => _instance ?? (_instance = new Container());
}
public ContainerConfiguration Composition { get; set; }
private Container()
{
//在Program类所在的程序集中,直接或间接加了 [Export] 特性的类都将被此依赖容器管理。
Composition = new ContainerConfiguration().WithAssembly(typeof(Program).Assembly);
}
}
class Program
{
public static IModel Model { get; set; }
static void Main(string[] args)
{
//创建容器
var container= Container.Instance.Composition.CreateContainer();
//获取实现IModel接口且有[Export(typeof(IModel))]标记的类的对象
Model = container.GetExport<IModel>();
Model.Do();
Console.ReadKey();
}
}
输出:
2. 实现接口IModel的类有多个时,ModelA , ModelB
var models = container.GetExports<IModel>().ToArray();
foreach (var item in models)
{
item.Do();
}
输出:
3. 通过构造函数注入,在构造函数添加特性[ImportingConstructor]
public interface IView
{
void Show();
}
[Export(typeof(IView))]
public class ViewA : IView
{
public Imodel Model { get; set;}
[ImportingConstructor]
public ViewA(IModel model) //实现IModel的类必须添加[Export]特性
{
Model = model; //通过构造函数注入对象
}
public void Show()
{
Console.WriteLine(MethodBase.GetCurrentMethod().DeclaringType.FullName);
Model.Do();
}
}
var view= container.GetExports<IView>().FirstOrDefault();
view.Show();
4. [ImportMany]导入多个实现了该接口的对象
[Export(typeof(IView))]
public class ViewA : IView
{
public ObservableCollection<IModel> Model { get; }
[ImportingConstructor]
public ViewA([ImportMany]IEnumerable<IModel> model) //importmany:导入多个实现了该接口的类
{
Model = new ObservableCollection<IModel>(model);
}
public void Show()
{
Console.WriteLine(MethodBase.GetCurrentMethod().DeclaringType.FullName);
foreach (var item in Model)
{
item.Do();
}
}
}
输出:
5. 当实现接口的类有多个时,为方便管理,使用契约名称来筛选要导出导入的对象
[Export("ModelA",typeof(IModel))] 参数1:契约名称(按需求自己起) 作用:在Import的时候,添加契约,会导入指定含有契约名称的类,契约名可以重复(此时导入多个),这样一来,我们就可以用契约名给类进行分类,导入时可以根据契约名来导入。
public class ExtendA : IExtend
[Export("ModelA",typeof(IModel))]
public class ModelA : IModel
{
public void Do()
{
Console.WriteLine(MethodBase.GetCurrentMethod().DeclaringType.FullName);
}
}
//契约名称为空,则不能发现指定了契约名称的ModelA
var models = container.GetExports<IModel>().ToArray();
//写入指定的契约名称,只能发现指定了相同契约名称的导出
var models = container.GetExports<IModel>("ModelA").ToArray();
6. 导出的类添加特性 [Shared] ,使导出的类共享实例(实例只有一个)
public interface IExtend
{
void Init();
int Num { get; set; }
}
[Shared]
[Export("ext",typeof(IExtend))]
public class ExtendA : IExtend
{
public void Init()
{
Num += 10;
Console.WriteLine(MethodBase.GetCurrentMethod().DeclaringType.FullName);
Console.WriteLine($"Num : {Num}");
}
public int Num { get; set; }
}
var Extend_1 = container.GetExport<IExtend>("ext");
Extend_1.Init();
var Extend_2 = container.GetExport<IExtend>("ext");
Extend_2.Init();
通过Num的值的变化简单说明共享实例,
不添加[Shared],输出:
添加[Shared],输出:
7. Lazy<>延迟初始化
[Export(typeof(IView))]
public class ViewB : IView
{
/*
* Lazy<>延迟初始化:仅仅当对象第一次使用的时候,再初始化,如果一直没有调用,那就不初始化
* 应用场景:1.对象创建成本高且程序可能不会使用它 2. 对象创建成本高,且希望将其创建推迟到其他高成本操作完成后
* 只有在Lazy变量的Value属性被首次访问时对象才会真正的创建,该属性只读,因此仅有get访问器
*/
private readonly Lazy<IExtend> _extend;
public IExtend Extend => _extend.Value;
[ImportingConstructor] //构造函数导入
public ViewB([Import("ext")] Lazy<IExtend> extend) //无锲约名称时,里面的import可以不写
{
_extend = extend;
}
public void Show()
{
Console.WriteLine(MethodBase.GetCurrentMethod().DeclaringType.FullName);
Extend.Init();
}
}
参考:
【1】Managed Extensibility Framework (MEF) | Microsoft Docs
【2】.NET Core 和 .NET Framework 中的 MEF2 - walterlv - 博客园 (cnblogs.com)