以前在使用Prism框架時接觸到了可擴展性框架MEF(Managed Extensibility Framework),體驗到MEF帶來的極大的便利性與可擴展性。框架
此篇將編寫一個可組合的應用程序,幫助你們快速熟悉MEF並將其應用於實際項目中。ide
有關MEF中的名詞含義及功能實現,請你們移步:火車票
ui
介紹下將要編寫的Demo程序(下圖),使用winform開發。this
新建項目後需引用程序集:spa
System.ComponentModel.Composition
主程序的核心代碼以下:插件
public partial class Form1 : Form, IPartImportsSatisfiedNotification { [ImportMany(AllowRecomposition = true)] private IEnumerable<Lazy<IPlugin, IPluginMetadata>> plugins; private AggregateCatalog catalog; private CompositionContainer container; public Form1() { InitializeComponent(); if (catalog == null) catalog = new AggregateCatalog(); this.container = new CompositionContainer(catalog); this.container.ComposeParts(this); } #region Implementation of IPartImportsSatisfiedNotification public void OnImportsSatisfied() { flowLayoutPanel1.Controls.Clear(); if (plugins != null && plugins.Count() != 0) { plugins.ToList().ForEach((a) => { Button btn = new Button(); btn.Cursor = System.Windows.Forms.Cursors.Hand; btn.Width = 100; btn.Height = 50; btn.Text = a.Metadata.ThePluginName; btn.Click += (d, b) => { a.Value.Run(); }; flowLayoutPanel1.Controls.Add(btn); }); } } #endregion public void CompositionAction() { catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly())); } ...... }
1. IPartImportsSatisfiedNotification接口 : 在組件導入完成後,調用該接口中的方法(OnImportsSatisfied)。code
2. MEF中最經常使用目錄有三種:程序集目錄(AssemblyCatalog),文件目錄(DirectoryCatalog),聚合目錄(AggregateCatalog)component
程序集目錄(AssemblyCatalog): 顧名思義能夠向目錄中添加程序集已存在類型中尋找可用於導入的部件。orm
var catalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());
文件夾目錄(DirectoryCatalog):從文件夾中尋找可用於導入的部件blog
var catalog = new DirectoryCatalog("Extensions");
聚合目錄(AggregateCatalog):能夠向聚合目錄包含上述兩種方式
var catalog = new AggregateCatalog( new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly()), new DirectoryCatalog("Extensions"));
3. CompositionContainer : 組合容器,管理部件的組合並提供了一系列用於建立部件實例擴展方法等,詳細資料
4. 目錄與容器的通常使用方法:
var catalog = new AggregateCatalog(); var container = new CompositionContainer(catalog); container.ComposeParts(this);
5. 導入:ImportAttribute 與 ImportManyAttribute
用於如下三種用途:字段,屬性,方法
[import] private IData _data; [import] public IData Data{set;get;} [import] public Action ClickAction;
ImportManyAttribute : 經過組合容器將全部符合契約的導出進行填充 (真彆扭,說白了就是導入多個)
[ImportMany] private IEnumerable<IPlugin> plugins;
AllowRecomposition : 是否容許重組
AllowRecomposition = true : 好比在程序運行的過程當中,動態向聚合目錄中添加可導出的部件,能夠引起重組操做
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
須要注意的是:DirectoryCatalog 不會引起重組操做,可經過Refresh方法指定重組的策略。
6. System.Lazy<T> : MEF提供延遲導入。
下面來看一下,插件式如何實現的:
[ExportPluginAttribute(typeof(IPlugin), ThePluginName = "TheFristPlugin")] public class TheFristPlugin : IPlugin { public TheFristPlugin() { this.TheName = "TheFristPlugin"; } #region Implementation of IPlugin public string TheName { get; set; } public void Run() { MessageBox.Show("TheFristPlugin"); } #endregion }
1. 簡單說一下:導入與導出以前的關係
一個基於MEF開發的可擴展的程序,在容器中必然有不少的導出(Export),而這些Export又是怎麼樣找到本身的歸宿呢。
Export 與 Import 依靠一種契約,來肯定對方是不是本身的兄弟,說白了就是接口,好比上述程序所定義的IPlugin接口
public interface IPlugin { void Run(); }
使用 ExportAttribute 特性導出:
[Export("count")] public int count{ get{return 0;} } [Export(typeof(Action))] public void SendMsg(){return;} [Export] public class Person{}
有一個需求,主程序要求插件必需要指定插件名稱:
1. 在IPlugin接口中定義:Name字段
2. 使用元數據
3. 使用自定義導出特性(與第二種方案相似)
如何使用元數據?
1.定義元數據視圖,此處視圖使用接口類型
public interface IPluginMetadata { string ThePluginName { get; } }
2. 導出部件時,使用ExportMetaData特性
[ExportMetadata("ThePluginName", "TheFivePlugin")] [Export(typeof(mef.test.wform.Interface.IPlugin))] public class TheFivePlugin : mef.test.wform.Interface.IPlugin { public void Run() { MessageBox.Show("TheFivePlugin"); } }
3. 導入元數據
[ImportMany(AllowRecomposition = true)] private IEnumerable<Lazy<IPlugin, IPluginMetadata>> plugins;
4. 訪問元數據
Lazy<T,TMetadata>.Value.Metadata
結束
到此爲止,MEF 基本內容已講解結束,若是有遺漏也請博友留言指出。
文章中不少都是白話,非官方語言,怎麼理解的就怎麼寫,若是有不妥之處,還望各位博友指出。
新年快樂