C# winform利用反射和自定義特性加載功能模塊(插件式開發)


 

由於在實際的工作中, 碰見這樣的一個問題:

一個軟件, 銷售給A客戶 他需要所有功能,

但是銷售給B客戶, 他只需要其中的一部分,

1.如果我們在實際的開發過程中, 沒有把一些功能模塊區分開來的話, 那么帶來的麻煩, 勢必是要修改源代碼。

2.直到有一天,B客戶又說需要某些功能,這個時候, 我們又要修改一次源代碼, 更新給客戶 , 所以想了想, 如果每個功能塊都獨立開來, 動態去加載功能, 這樣就不用改動源代碼, 客戶需要哪些功能, 注冊那些DLL給他們使用。

 

?.實現思路
1.每個模塊都用單獨的程序集(DLL)分開  <反射動態加載>

2.需要指定每個功能模塊的命名空間       <特性標記命名空間

 

!.實現代碼

1.程序中定義自定義特性, 用於映射程序的的模塊信息和功能模塊所在的命名空間

 

 /// <summary>
    /// 模塊編號.
    /// </summary>
    public enum ModuleID
    {
        None = 0,
        DataDictionary = 1,
        SystemManage = 2
    }

    /// <summary>
    /// 模塊名稱.
    /// </summary>
    public class ModuleNames
    {
        public const string DataDictionary = "基礎數據";
        public const string SystemManage = "系統管理";
    }

    /// <summary>
    /// 模塊入口自定義特性
    /// </summary>
    public class AssemblyModuleEntry : Attribute
    {

        private ModuleID _moduleID;
        private string _moduleName;
        private string _moduleEntryNameSpace;

        /// <summary>
        /// 模塊編號
        /// </summary>
        public ModuleID ModuleID { get { return _moduleID; } }

        /// <summary>
        /// 模塊名稱
        /// </summary>
        public string ModuleName { get { return _moduleName; } }

        /// <summary>
        /// 模塊名字空間
        /// </summary>
        public string ModuleEntryNameSpace { get { return _moduleEntryNameSpace; } }

        /// <summary>
        /// 構造器
        /// </summary>
        /// <param name="moduleID">模塊編號</param>
        /// <param name="moduleName">模塊名稱</param>
        /// <param name="moduleEntryNameSpace">模塊名字空間</param>
        public AssemblyModuleEntry(ModuleID moduleID, string moduleName, string moduleEntryNameSpace)
        {
            _moduleID = moduleID;
            _moduleName = moduleName;
            _moduleEntryNameSpace = moduleEntryNameSpace;
        }

    }

 

在模塊的程序集設置好新建的自定義特性 (如下):

 

// 有關程序集的一般信息由以下
// 控制。更改這些特性值可修改
// 與程序集關聯的信息。
[assembly: AssemblyModuleEntry(ModuleID.DataDictionary, ModuleNames.DataDictionary, "My.DataDictionary.Form1")]

 

 

 

1.定義方法讀取程序集(DLL)中的第一個特性信息

/// <summary>
        /// 獲取程序集自定義特性。是否用戶自定義模塊由AssemblyModuleEntry特性確定。
        /// </summary>
        public static AssemblyModuleEntry GetModuleEntry(Assembly asm)
        {
            AssemblyModuleEntry temp = new AssemblyModuleEntry(ModuleID.None, "", "");
            if (asm == null) return temp;

            object[] list = asm.GetCustomAttributes(typeof(AssemblyModuleEntry), false);
            if (list.Length > 0)
                return (AssemblyModuleEntry)list[0];
            else
                return temp;
        }

 

2.保存AssemblyModuleEntry 的特性信息, 解析特性信息利用反射映射功能模塊的主窗體

 /// <summary>
        /// 加載模塊主方法
        /// </summary>
        /// <param name="moduleinfo">模塊信息</param>
        /// <returns></returns>
        public virtual bool LoadModule(ModuleInfo moduleinfo)
        {
            _ModuleFileName = moduleinfo.ModuleFile;
            _ModuleAssembly = moduleinfo.ModuleAssembly;
            string entry = GetModuleEntryNameSpace(_ModuleAssembly);
            if (string.Empty == entry) return false;

            Form form = (Form)_ModuleAssembly.CreateInstance(entry);  //根據命名空間加載Form
            _ModuleMainForm = null;

            if (form is IModuleBase) _ModuleMainForm = (IModuleBase)form;

            return _ModuleMainForm != null;
        }

 

3.將功能模塊的功能都加載到全局緩存對象中, 創建首頁的UI控件, 將緩存對象中的模塊加載到首頁中。

 

 

 

實際效果圖:

注意: 反射是會帶來性能的損耗, 但是經過合理的優化,還是對性能影響不大, 當個這個設計, 主要看每個人使用的取舍。

 

 

核心思想:

1.定義自定義特性

2.將功能命名空間存儲到自定義特性中(主要用於反射獲取到指定的功能界面區)

3.利用反射去獲取目錄下的有特性的程序集,加載指定的功能

4.將反射獲取的指定界面轉換成緩存對象

5.將緩存對象轉換成首頁指定的UI控件上。

 

PS: 關於權限的控制思路:

針對所登陸得用戶, 獲取當前用戶所有的權限, 根據權限加載權限內的程序集。

 

 

 

 

 

 

 

 

---恢復內容結束---


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM