首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 插件開發簡易模型實作指南

     編輯:關於C#

前言

插件模型是指應用程式由多個動態、獨立的模組組成,每個模組擁有一或多個服務,並遵循特定的插件協議,透過主程式實現主程式與插件、插件與插件之間的通訊。其核心在於定義一套公共介面,利用反射機制獲取相關屬性與方法,進而執行指定操作。換句話說,這是一種定義通用解決方案的模式,透過反射取得組件的相關型別並執行操作,具備即插即用、更新維護簡便的特點。

本文將介紹一個簡單的插件開發模型,主要步驟包含三大階段:定義公共介面與抽象類別、實作相關組件、以及建立通用組件反射操作類別。

架構設計:核心與實作分離

在這個模型中,公共介面與抽象類別定義於 Jasen.Framework.Core 組件,其中包含通用的組件反射操作類別 AssemblyUtility。具體實作的組件則分別為 Jasen.Framework.Oracle、Jasen.Framework.Access 與 Jasen.Framework.SqlServer,它們皆實作核心組件中的公共介面。客戶端可根據實際需求動態載入對應組件。

組件關係圖
圖 1:組件關係示意圖

公共介面與抽象類別定義

首先定義公共介面 IDataTable 與抽象類別 DataTable,置於最上層組件。其他組件分別建立專案,實作對應功能,如 SqlServerDataTable、OracleDataTable、AccessDataTable。特別注意:使用 Assembly.LoadFile 動態載入組件時,該組件在當前執行環境中必須不存在,否則可能導致資料異常,因此各實作組件必須獨立(僅實作公共介面)。

類別圖
圖 2:類別關係圖

通用組件反射操作類別實作

AssemblyUtility 是操作組件的通用類別,支援依指定目錄或檔案清單動態載入組件,並能取得型別集合與物件實例集合。關鍵在於實作的子類別必須提供預設建構函式。以下為核心程式碼:

public static class AssemblyUtility
{
    public static IEnumerable<Type> GetImplementdTypesByDirectory<T>(string baseDirectory)
    {
        IList<Assembly> assemblies = GetAssemblies(baseDirectory);
        List<Type> types = new List<Type>();
        foreach (Assembly assembly in assemblies)
        {
            types.AddRange(GetImplementdTypes<T>(assembly));
        }
        return types;
    }

    public static IEnumerable<Type> GetImplementdTypes<T>(string assemblyFile)
    {
        if (!File.Exists(assemblyFile)) return null;
        try { return GetImplementdTypes<T>(Assembly.LoadFile(assemblyFile)); }
        catch { return null; }
    }

    public static IEnumerable<Type> GetImplementdTypes<T>(Assembly assembly)
    {
        if (assembly == null) return null;
        return assembly.GetExportedTypes()
            .Where(p => p.IsSubclassOf(typeof(T)) && !p.IsAbstract && !p.IsInterface);
    }

    public static IList<T> GetImplementedObjectsByDirectory<T>(string baseDirectory)
    {
        IList<Assembly> assemblies = GetAssemblies(baseDirectory);
        List<T> entities = new List<T>();
        foreach (Assembly assembly in assemblies)
        {
            entities.AddRange(GetImplementedObjects<T>(assembly));
        }
        return entities;
    }

    public static IList<T> GetImplementedObjects<T>(string assemblyFile)
    {
        if (!File.Exists(assemblyFile)) return null;
        try { return GetImplementedObjects<T>(Assembly.LoadFile(assemblyFile)); }
        catch { return null; }
    }

    public static IList<T> GetImplementedObjects<T>(Assembly assembly)
    {
        if (assembly == null) return null;
        IEnumerable<Type> types = GetImplementdTypes<T>(assembly);
        var result = new List<T>();
        foreach (Type type in types)
        {
            ConstructorInfo constructor = type.GetConstructor(new Type[0]);
            if (constructor == null) continue;
            object instance = Activator.CreateInstance(type);
            if (instance is T) result.Add((T)instance);
        }
        return result;
    }

    public static IList<Assembly> GetAssemblies(string baseDirectory)
    {
        if (!Directory.Exists(baseDirectory)) return new List<Assembly>();
        string[] files = Directory.GetFiles(baseDirectory, "*.dll");
        return GetAssemblies(files);
    }

    public static IList<Assembly> GetAssemblies(string[] assemblyFiles)
    {
        IList<Assembly> assemblies = new List<Assembly>();
        try
        {
            foreach (string file in assemblyFiles)
            {
                if (!File.Exists(file) || !file.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase)) continue;
                assemblies.Add(Assembly.LoadFile(file));
            }
        }
        catch { return new List<Assembly>(); }
        return assemblies;
    }
}

方法說明

  • 取得型別集合:GetImplementdTypesByDirectory、GetImplementdTypes (依檔案/組件) — 依參數動態取得指定基底類別 T 的非抽象、非介面子類別型別。
  • 取得物件實例集合:GetImplementedObjectsByDirectory、GetImplementedObjects (依檔案/組件) — 同樣依參數動態建立型別 T 的實例。若子類別有參數建構函式,必須實作無參數建構函式,否則略過該型別。

單元測試驗證

測試涵蓋組件載入、型別與物件取得等情境,確保 AssemblyUtility 運作正確。範例測試碼如下:

[TestMethod]
public void GetImplementedObjectsByDirectoryTest()
{
    string assemblyDir = AppDomain.CurrentDomain.BaseDirectory + "\\Files\\";
    IList<DataTable> result = AssemblyUtility.GetImplementedObjectsByDirectory<DataTable>(assemblyDir);
    Assert.IsNotNull(result);
    Assert.AreEqual(3, result.Count);
}

總結與應用建議

本文以 AssemblyUtility 為核心示範插件開發思路。實際應用時,客戶端只需取得型別或物件集合,再執行具體操作。插件組件放置於指定目錄,透過 AssemblyUtility 動態載入即可取得所需型別資料。具體功能邏輯封裝於各插件組件中,主程式無需知悉細節,達到高度解耦。

原始碼下載:C# 插件開發模型原始碼

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved