.Net插件框架Managed Extensibility Framework(MEF)是一个用于在应用程序中使用插件的框架。它利用了C#语言的特性和CLR(共同语言运行时)的能力,为应用程序提供了一种灵活的架构,使得可以追加或更改应用程序中的功能,而无需重新编译或修改代码。
什么是MEF
MEF是Microsoft推出的,用于构建可扩展和高度可组合的应用程序的框架。MEF框架的作用是将一个应用程序分成许多小部分,每一部分都可以提供一个特定的功能。MEF框架允许我们在应用程序中使用插件,并且通过在代码中添加几行简单的指令,就可以实现扩展功能的追加或更改,在不修改现有代码的情况下,让应用程序具有更高的灵活性和可扩展性。
使用MEF框架的好处就是我们可以实现代码的解耦和组件的松散耦合。这意味着我们可以在应用程序中添加新的功能,而无需在应用程序中添加新的代码。
MEF的应用场景
MEF通常用于需要扩展性的应用程序。在这类应用程序中,我们希望有一种机制,使得在应用程序运行时,能够动态添加或替换功能。几个例子包括:
- 插件式应用程序
- 计算机辅助设计(CAD)软件
- 视频编辑软件
- 游戏引擎
- 博客引擎
无论是哪种应用场景,都可以利用MEF框架来实现应用程序的扩展。
MEF的组成部分
MEF框架主要由4个部分组成,它们分别是:
- Part
- Export
- Import
- Container
严格按照这4个模块进行组装,我们可以将一个应用程序拆分成几个独立的部分,并在运行时动态地将它们整合起来。下面是这些部分的详细介绍:
Part
Part是一个被视为可供扩展的单元,通常是一个类、接口、方法或类型。在将Part加入MEF容器之前,Part必须标记为“可导出的”(Exportable)。一旦Part被标记为可导出的,它就可以被添加到MEF容器中。
Export
Export是一个可被Part导入的类型或对象。Export是Part被导出后的结果,可以被其他需要这些功能的应用程序组件引用。Export可以被定义为一个单一的值,也可以是一个数组,其类型通常是接口或对象。
Import
Import是Part所需要的Export的引用。Import用于描述Part所依赖的Export。Import可以是必需的、可选的或多个,代表Part所需要的Export的个数。
Container
Container是MEF框架的核心部分。它是一个用于管理和组合Export和Import的对象。Container可以让应用程序在运行时使用Export,而无需在代码中执行显式的实例化。
MEF的优点
MEF框架提供了许多有用的功能,这些功能可以减少复杂性并增强应用程序的灵活性和可扩展性。下面是MEF框架的几个优点:
- 简化了插件的扩展。开发人员可以从容器中根据需要获取插件,而不必担心插件的具体实现方式。
- 可以减少代码重复,从而提高代码的可维护性。
- 可以用于替换应用程序的组件而不影响其他组件的运行。
- 使用MEF可以大大增强应用程序的可测试性,因为开发人员可以轻松地模拟和替换Export。
MEF的示例说明
示例1:使用Export和Import特性,将方法定义为可导出和导入的插件
假设我们有一个应用程序,希望通过插件实现一些复杂的数学计算功能。在这个例子中,我们将定义一个名为IMathPlugin的接口,所有的数学计算都将继承这个接口。此外,为了能够将计算方法导出到应用程序中,请将IMathPlugin方法修饰为Export特性。
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
public interface IMathPlugin
{
double Calculate(double x, double y);
}
[Export(typeof(IMathPlugin))]
public class AddPlugin : IMathPlugin
{
public double Calculate(double x, double y)
{
return x + y;
}
}
public class MainProgram
{
[ImportMany(typeof(IMathPlugin))]
public IMathPlugin[] Plugins { get; set; }
public static void Main(string[] args)
{
var program = new MainProgram();
program.Run();
}
public void Run()
{
var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
foreach (var plugin in Plugins)
{
Console.WriteLine(plugin.Calculate(10, 20));
}
}
}
在这个例子中,我们定义了一个AddPlugin类,它实现了IMathPlugin接口。我们将AddPlugin类标记为可导出的,并且在MainProgram中使用ImportMany特性导入了IMathPlugin接口。在MainProgram的Run方法中,我们可以使用CompositionContainer对象轻松地组合AddPlugin插件。
示例2:使用MEF插件框架,动态添加和移除UI插件
假设我们有一个支持插件开发的Windows桌面软件。在这个例子中,我们将演示如何使用MEF框架动态添加和移除UI插件。
首先,我们需要定义一个接口来表示UI插件:
using System.ComponentModel.Composition;
[InheritedExport]
public interface IUIPlugin
{
void Initialize();
void Unload();
}
此接口定义了两个方法:Initialize(初始化)和Unload(卸载)。Initialize方法用于初始化插件,而Unload方法用于卸载插件。我们使用InheritedExport特性将接口标记为可导出的。
然后,我们考虑如何实现一个可扩展的UI组件。
我们将定义一个窗体,这个窗体可以动态添加和删除UI插件。对于窗体的具体设计,我们将使用WPF。窗体的xaml代码如下:
<Window x:Class="MEF.PluginExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<Button Name="AddPluginButton" Click="AddPluginButtonClick" Margin="5" Width="75">Add Plugin</Button>
<Button Name="RemovePluginButton" Click="RemovePluginButtonClick" Margin="5" Width="75" IsEnabled="False">Remove Plugin</Button>
</StackPanel>
<Grid Grid.Row="1" Name="PluginContainer"/>
</Grid>
</Window>
在这个窗体中,我们定义了一个名为PluginContainer的Grid控件,它将用于UI插件的加载和卸载。在Grid上面的工具栏中,我们添加了两个按钮:Add Plugin(添加插件)和Remove Plugin(删除插件)。这些按钮将允许我们动态添加和删除UI插件。
现在,我们需要在代码中实现这些按钮的逻辑。我们使用MEF框架的CompositionContainer对象来获取UI插件。具体实现如下:
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
namespace MEF.PluginExample
{
public partial class MainWindow : Window
{
private readonly CompositionContainer _container = new CompositionContainer();
public MainWindow()
{
InitializeComponent();
var catalog = new AggregateCatalog(
new AssemblyCatalog(Assembly.GetExecutingAssembly()),
new DirectoryCatalog(".")
);
_container.ComposeParts(this, catalog);
}
private void AddPluginButtonClick(object sender, RoutedEventArgs e)
{
var dialog = new Microsoft.Win32.OpenFileDialog
{
Filter = "Plugins (*.dll)|*.dll|All files (*.*)|*.*"
};
if (dialog.ShowDialog() != true) return;
var assembly = Assembly.LoadFile(dialog.FileName);
_container.Catalog.Catalogs.Add(new AssemblyCatalog(assembly));
_container.ComposeParts(this);
PluginContainer.Children.Clear();
foreach (var plugin in _container.GetExportedValues<IUIPlugin>())
{
var button = new Button
{
Content = plugin.GetType().Name,
Margin = new Thickness(5),
Tag = plugin
};
button.Click += PluginButtonClick;
PluginContainer.Children.Add(button);
}
RemovePluginButton.IsEnabled = true;
}
private void PluginButtonClick(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
var plugin = (IUIPlugin)button.Tag;
plugin.Initialize();
button.IsEnabled = false;
AddPluginButton.IsEnabled = false;
RemovePluginButton.IsEnabled = true;
}
private void RemovePluginButtonClick(object sender, RoutedEventArgs e)
{
var selectedButton = (Button)PluginContainer.Children[0];
var selectedPlugin = (IUIPlugin)selectedButton.Tag;
selectedPlugin.Unload();
_container.Catalog.Catalogs.Remove(_container.Catalog.Catalogs[_container.Catalog.Catalogs.Count - 1]);
_container.ComposeParts(this);
PluginContainer.Children.Clear();
foreach (var plugin in _container.GetExportedValues<IUIPlugin>())
{
var button = new Button
{
Content = plugin.GetType().Name,
Margin = new Thickness(5),
Tag = plugin
};
button.Click += PluginButtonClick;
PluginContainer.Children.Add(button);
}
AddPluginButton.IsEnabled = true;
RemovePluginButton.IsEnabled = false;
}
}
}
如上所述,我们使用MEF框架的CompositionContainer对象来获取可用的UI插件。在AddPluginButtonClick事件处理程序中,我们使用Microsoft.Win32.OpenFileDialog控件来获取用户选择的插件。然后,我们将插件的Assembly添加到MEF容器中,重新组合Parts,并初始化UI插件。对于AddPluginButtonClick事件处理程序中的剩余代码,我们使用C#的WPF技术来允许用户动态添加按钮并将插件添加到用户界面中。
对于RemovePluginButtonClick事件处理程序中,我们首先使用PluginContainer.Children[0]获取当前选定的按钮,然后使用Tag属性获取当前选定的插件。然后,我们卸载这个插件,并从反映目录中删除它。最后,我们重新组合Parts,并重新初始化用户界面中的所有插件。
总之,MEF插件框架是一个用于在应用程序中使用插件的灵活框架,它支持将应用程序进行拆分并重组成部分,追加或更改应用程序的功能,大大提高了应用程序的可扩展性。在使用MEF框架时,我们只需遵循上述规则并利用好框架提供的各种组件,便可轻松实现应用程序的扩展。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:.Net插件框架Managed Extensibility Framework简介 - Python技术站