軟體部署後面臨需求變更,傳統單體架構需重新編譯部署,風險高、週期長。外掛架構將核心功能與擴充點分離,支援熱部署、獨立開發、版本隔離,是應對需求蔓延的關鍵技術。
| 類型 | 機制 | 優點 | 缺點 |
|---|---|---|---|
| 腳本式 | Python/Lua/XML 腳本 | 用戶可自訂、無需編譯 | 效能差、安全風險、除錯難 |
| 動態函式庫 (DLL) | P/Invoke 或 Managed DLL | 效能佳、語言中立 | 版本管理複雜、型別安全弱 |
| 聚合式 (EXE) | 啟動獨立進程協作 | 隔離性最強 | 進程間通訊複雜、延遲高 |
| COM/介面元件 | 介面契約 + 反射載入 | 型別安全、鬆耦合、版本獨立、工具支援完善 | 學習曲線較陡 |
C# 原生支援 interface、System.Reflection、AppDomain 隔離,是實作外掛架構的理想平台。本文以程式編輯器外掛系統為例完整示範。
public interface IApplicationObject {
void Alert(string msg);
void ShowInStatusBar(string msg);
IDocumentObject QueryCurrentDocument();
IDocumentObject[] QueryDocuments();
void SetDelegate(Delegates whichOne, EventHandler target);
}
public enum Delegates {
Delegate_ActiveDocumentChanged
}
public interface IDocumentObject {
// RichTextBox 屬性映射
string SelectionText { get; set; }
Color SelectionColor { get; set; }
Font SelectionFont { get; set; }
int SelectionStart { get; set; }
int SelectionLength { get; set; }
string SelectionRTF { get; set; }
bool HasChanges { get; }
void Select(int start, int length);
void AppendText(string str);
void SaveFile(string fileName);
void SaveFile();
void OpenFile(string fileName);
void CloseFile();
}
public interface IPlugin {
ConnectionResult Connect(IApplicationObject app);
void OnDestroy();
void OnLoad();
void Run();
}
public enum ConnectionResult {
Connection_Success,
Connection_Failed
}
[AttributeUsage(AttributeTargets.Class)]
public class PluginInfoAttribute : Attribute {
public PluginInfoAttribute() { }
public PluginInfoAttribute(string name, string version, string author, string webpage, bool loadWhenStart) { ... }
public string Name { get; }
public string Version { get; }
public string Author { get; }
public string Webpage { get; }
public bool LoadWhenStart { get; }
public object Tag { get; set; }
public int Index { get; set; }
}
[PluginInfo("My Plugin 1", "1.0", "Jack H Hansen", "https://blog.csdn.net/matrix2003b", true)]
public class MyPlugin1 : IPlugin {
public MyPlugin1() { }
// IPlugin 成員實作...
private IApplicationObject _App;
private IDocumentObject _CurDoc;
}
private bool IsValidPlugin(Type t) {
Type[] interfaces = t.GetInterfaces();
foreach (Type iface in interfaces) {
if (iface.FullName == "CSPluginKernel.IPlugin")
return true;
}
return false;
}
// 啟動時掃描 plugins 目錄
foreach (string file in Directory.GetFiles("plugins")) {
Assembly pluginAssembly = Assembly.LoadFrom(file);
foreach (Type pluginType in pluginAssembly.GetTypes()) {
if (IsValidPlugin(pluginType)) {
IPlugin plugin = (IPlugin)pluginAssembly.CreateInstance(pluginType.FullName);
plugins.Add(plugin);
}
}
}
// 連接階段
foreach (IPlugin plugin in plugins) {
ConnectionResult result = plugin.Connect(appObject);
if (result == ConnectionResult.Connection_Success)
plugin.OnLoad();
}
// 觸發執行(如選單點擊)
plugin.Run();
// 關閉階段
foreach (IPlugin plugin in plugins)
plugin.OnDestroy();
MEF (Managed Extensibility Framework) 簡化組合邏輯基於介面契約與反射的外掛架構,充分發揮 C# 型別系統與執行環境優勢,在型別安全、鬆耦合、擴充性之間取得最佳平衡。此架構模式廣泛應用於 Visual Studio、SharpDevelop、Paint.NET 等知名專案,經實戰驗證可靠。