MEF 核心在於 Export(匯出) 與 Import(匯入) 的契約匹配。本文從定義共用介面 IMember 開始,逐步建構:以 [Export(typeof(IMember))] 標記實作類別、用 [ImportMany(typeof(IMember))] 收集所有實作、再引入 Lazy<T> 實現延遲實例化,完整呈現 MEF 最基礎也最實用的工作流。
就像 C++ DLL 必須 __declspec(dllexport) 才能被外部呼叫,MEF 需要明確知道「哪些類別是擴充件」。未標記 [Export] 的類別對 MEF 不可見。
// 僅指定 ContractType(推薦)
[Export(typeof(IMember))]
public class VipMember : IMember { /* ... */ }
[Export(typeof(IMember))]
public class GenMember : IMember { /* ... */ }
若不指定 ContractType,預設為實作類別本身(VipMember),導致匯入端需逐一匹配型別,失去自動發現優勢。永遠指定共用介面作為 ContractType。
class Program {
// 錯誤:Import 不支援集合
// [Import(typeof(IMember))] public List AllMembers;
// 正確:ImportMany 專為集合設計
[ImportMany(typeof(IMember))]
public List<IMember> AllMembers { get; set; }
}
[ImportMany(typeof(IMember))]
public List<Lazy<IMember>> AllMembers { get; set; }
// 使用時才實例化
foreach (var lazy in AllMembers) {
Console.WriteLine(lazy.Value.GetMemberType()); // .Value 觸發建構
}
Lazy<T> 如同支票:組裝階段只存「怎麼建立」,真正呼叫 .Value 才刷卡(建構物件),適合擴充件攜帶大型資料或建構昂貴的情境。
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
namespace MEFExam {
public interface IMember {
string GetMemberType();
}
[Export(typeof(IMember))]
public class VipMember : IMember {
public string GetMemberType() => "VIP會員";
}
[Export(typeof(IMember))]
public class GenMember : IMember {
public string GetMemberType() => "普通會員";
}
class Program {
[ImportMany(typeof(IMember))]
public List<Lazy<IMember>> AllMembers { get; set; }
static void Main() {
var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog);
var p = new Program();
container.ComposeParts(p);
foreach (var lazy in p.AllMembers)
Console.WriteLine(lazy.Value.GetMemberType());
container.Dispose();
Console.Read();
}
}
}


System.ComponentModel.Composition.dllSystem.ComponentModel.Composition、System.ComponentModel.Composition.Hosting掌握 Export/Import/ImportMany/Lazy 後,下一篇將探討「只匯出類別成員」與「元資料篩選」,讓 MEF 應用更精準、更輕量。