C#反射實現插件式開發

来源:https://www.cnblogs.com/wml-it/archive/2023/09/15/17706182.html
-Advertisement-
Play Games

前言 插件式架構,一種全新的、開放性的、高擴展性的架構體系。插件式架構設計好處很多,把擴展功能從框架中剝離出來,降低了框架的複雜度,讓框架更容易實現。擴展功能與框架以一種很松的方式耦合,兩者在保持介面不變的情況下,可以獨立變化和發佈。基於插件設計並不神秘,相反它比起一團泥的設計更簡單,更容易理解。 ...


前言

插件式架構,一種全新的、開放性的、高擴展性的架構體系。插件式架構設計好處很多,把擴展功能從框架中剝離出來,降低了框架的複雜度,讓框架更容易實現。擴展功能與框架以一種很松的方式耦合,兩者在保持介面不變的情況下,可以獨立變化和發佈。基於插件設計並不神秘,相反它比起一團泥的設計更簡單,更容易理解。

項目介紹

書寫4個插件類庫,分別傳參實現“加減乘除”運算,調用插件的客戶端採用Winform窗體程式。

目標框架:.NET Framework 4.6.1

項目架構和窗體佈局:

image

客戶端程式:

  • PluginApp:反射調用插件

插件描述:

  • PluginBase:規範插件的基類,定義抽象類,開發的插件的類需要繼承此類,代表遵守這個規範。
  • CustomPlugInA:實現加法的插件
  • CustomPlugInB:實現減法的插件
  • CustomPlugInC:實現乘法的插件
  • CustomPlugInD:實現除法的插件

代碼實現

插件基類
 /// <summary>
    ///插件基類
    /// </summary>
    public abstract class Base
    {
        /// <summary>
        /// 插件名稱
        /// </summary>
        /// <returns></returns>
        public abstract string Name();
       /// <summary>
       /// 插件描述
       /// </summary>
       /// <returns></returns>
        public abstract string Desc();
        /// <summary>
        /// 執行方法
        /// </summary>
        /// <param name="param1">參數1</param>
        /// <param name="param2">參數2</param>
        /// <returns></returns>
        public abstract string Run(int param1, int param2);
        /// <summary>
        /// 版本 
        /// </summary>
        public string Version
        {
            get { return "1.0.0"; }
        }
    }
PlugInA
    public class PlugInA: Base
    {

        public override string Name()
        {
            return "PlugInA";
        }

        public override string Desc()
        {
            return "加法";
        }

        public override string Run(int param1,int param2)
        {
            return (param1 + param2) + "";
        }
    }
}
PlugInB
    public class PlugInB : Base
    {

        public override string Name()
        {
            return "PlugInB";
        }

        public override string Desc()
        {
            return "減法";
        }

        public override string Run(int param1, int param2)
        {
            return (param1 - param2) + "";
        }
    }
PlugInC
  public class PlugInC : Base
    {

        public override string Name()
        {
            return "PlugInC";
        }

        public override string Desc()
        {
            return "乘法";
        }

        public override string Run(int param1, int param2)
        {
            return (param1 * param2) + "";
        }
    }
PlugInD
 public class PlugInD : Base
    {

        public override string Name()
        {
            return "PlugInD";
        }

        public override string Desc()
        {
            return "除法";
        }

        public override string Run(int param1, int param2)
        {
            return (param1 / param2) + "";
        }
    }
客戶端核心代碼:
   public partial class FrmMain : Form
    {
        public FrmMain()
        {
            InitializeComponent();
            dgrvPlugins.AutoGenerateColumns = false;
        }

        List<PluginModel> List = new List<PluginModel>();

        readonly string PlugInPath = Application.StartupPath + "\\PlugIns";

        /// <summary>
        /// 載入插件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btLoadPlugins_Click(object sender, EventArgs e)
        {

            if (!Directory.Exists(PlugInPath))
            {
                Directory.CreateDirectory(PlugInPath);
            }
            List.Clear();
            string[] files = Directory.GetFiles(PlugInPath);
            foreach (string file in files)
            {
                if (file.ToLower().EndsWith(".dll"))
                {
                    try
                    {
                        Assembly assembly = Assembly.LoadFrom(file);
                        Type[] types = assembly.GetTypes();
                        foreach (Type type in types)
                        {
                            if (type.BaseType.FullName == "PlugInBase.Base")
                            {
                                object obj = assembly.CreateInstance(type.FullName);
                                string name = type.GetMethod("Name").Invoke(obj, null).ToString();
                                string desc = type.GetMethod("Desc").Invoke(obj, null).ToString();
                                string version = type.GetProperty("Version").GetValue(obj).ToString();

                                List.Add(new PluginModel
                                {
                                    Name = name,
                                    Desc = desc,
                                    Version = version,
                                    type = type,
                                    Obj = obj
                                });
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                }
            }

            dgrvPlugins.DataSource = new BindingList<PluginModel>(List);
        }

        /// <summary>
        /// 打開插件目錄
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btOpenPluginDir_Click(object sender, EventArgs e)
        {
            Process.Start(PlugInPath);
        }


        /// <summary>
        /// 執行選中插件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btExcute_Click(object sender, EventArgs e)
        {
            //獲取選擇的插件信息
            int index = dgrvPlugins.CurrentRow.Index;
            object obj = List[index].Obj;
            Type type = List[index].type;
            //參數
            object[] inParams = new object[2];
            inParams[0] =Convert.ToInt32( dgrvPlugins.CurrentRow.Cells[2].Value);
            inParams[1] = Convert.ToInt32(dgrvPlugins.CurrentRow.Cells[3].Value);
            object value = type.GetMethod("Run").Invoke(obj, inParams);
            MessageBox.Show(Convert.ToString(value),"結果",MessageBoxButtons.OK);
        }
    }

項目配置

插件生成配置

編譯生成項目的時候需要註意,此處的調用插件是通過反射調用.dll中類和方法,所以首先要找到這個.dll的文件,所以此處我們在Winform客戶端程式下建立一個存放類庫dll的文件PlugIns,在插件類庫項目生成後事件命令中,填入如下命令:

copy /Y "$(TargetDir)$(ProjectName).dll" "$(SolutionDir)\PlugIns"

以上命令代表,在項目的類庫生成後,將類庫copy到解決方案的路徑子文件夾PlugIns,也就是我們建立存放自定義插件的文件夾。當然,如果不怕麻煩,每次生成後,手動複製到此文件夾也可以,直接複製到客戶端程式的..\bin\PlugIns文件夾下。

image

插件路徑配置

全選這些類庫,把這些類庫設置為"如果較新則複製",這樣每次在編譯客戶端程式,如果自定義插件有更新,則同步會複製到bin目錄下

image

插件基類配置

插件基類提供了規範,需要在類庫的生成後事件,添加命令:

copy /Y "$(TargetDir)$(ProjectName).dll" "$(SolutionDir)\bin\Debug"

將生成的dll文件,拷貝到客戶端程式的bin路徑下

image

調用演示

CustomPlugInA

image

CustomPlugInB

image

CustomPlugInC

image

CustomPlugInD

image

插件開發優缺點

  • 把擴展功能從框架中剝離出來,降低了框架的複雜度,讓框架更容易實現
  • 宿主中可以對各個模塊解析,完成插件間、插件和主程式間的通信。
  • 插件開發的可擴展性,靈活性比較高,而且可以進行定製化開發。
缺點
  • 每一個插件被編譯成了dll,各模塊無法單獨運行,必須依托於主程式。

  • 修改插件時,由於生成的是dll,無法快速直觀的查看修改以及調試。

  • 每一個插件必須依賴於某一個規範。

本文來自博客園,作者:碼農阿亮,轉載請註明原文鏈接:https://www.cnblogs.com/wml-it/p/17706182.html


技術的發展日新月異,隨著時間推移,無法保證本博客所有內容的正確性。如有誤導,請大家見諒,歡迎評論區指正!
開源庫地址,歡迎點亮:
GitHub:https://github.com/ITMingliang
Gitee:   https://gitee.com/mingliang_it
GitLab: https://gitlab.com/ITMingliang

建群聲明: 本著技術在於分享,方便大家交流學習的初心,特此建立【編程內功修煉交流群】,為大家答疑解惑。熱烈歡迎各位愛交流學習的程式員進群,也希望進群的大佬能不吝分享自己遇到的技術問題和學習心得!進群方式:掃碼關註公眾號,後臺回覆【進群】。



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本篇藉助JProfiler工具,從線程的觀察結果去印證官方資料,做到理論結合實踐,讓您領先一步,掌握和瞭解神秘的虛擬線程內幕 ...
  • Notion相信大家都不陌生了,一款非常好用的筆記軟體,TJ君也一直在用來記筆記和寫文章。關於Notion的替代品,之前有給大家推薦AFFiNE ,但這個還是一個比較成型的軟體。 那麼如果想開發一個類Notion的工具,又或者在自己的應用中增加一個類Notion的內容編輯功能,是否有好用的開源工具呢 ...
  • 1.什麼是迴圈依賴? 迴圈依賴是指一個或多個對象之間存在直接或間接的依賴關係,這種依賴關係構成一個環形調用 , 舉個例子 : A 依賴B , B依賴C , C依賴A , 這樣就形成了迴圈依賴; 2.spring對迴圈依賴的處理有三種情況: ①構造器的迴圈依賴:這種依賴spring是處理不了的,直接拋 ...
  • JavaSe 變數和運算符: 基本數據類型介紹 java中浮點數精度怎麼解決,有瞭解過實現嗎,為什麼有精度問題 BigDecimal,如何判斷BigDecimal是否相等。如何進行計算、怎麼四捨五入 基本類型幾種,分別占用空間 int和Integer區別--包裝類,int有幾個位元組。 包裝類常量池 ...
  • 今天在看開源項目的時候發現了這樣一句代碼 import static com.abin.mallchat.common.common.service.frequencycontrol.FrequencyControlStrategyFactory.TOTAL_COUNT_WITH_IN_FIX_TI ...
  • 背景 在分散式系統中,經常需要用到全局唯一ID發生器,標識需要存儲的數據。我們需要什麼樣的ID生成器? ID生成器除了是數據的唯一標識以外,一般需要在系統中承擔更多的責任,概括起來有以下幾點: 唯一性:“全局唯一” vs “業務唯一”? 分散式系統使用唯一的ID生成器,會有非常嚴重的申請互斥問題。互 ...
  • IAT(Import Address Table)Hook是一種針對Windows操作系統的API Hooking 技術,用於修改應用程式對動態鏈接庫(DLL)中導入函數的調用。IAT是一個數據結構,其中包含了應用程式在運行時使用的導入函數的地址。IAT Hook的原理是通過修改IAT中的函數指針,... ...
  • 假設你有一行 String condition = "A or B and C"; 語句,請問怎麼做才能變成一行真正的邏輯表達式(能在電腦中運行計算)? Resolution 聲明一個List<List<String>>結構; 先分割 or ; 變成 [ A, B and C ] 不包含and的, ...
一周排行
    -Advertisement-
    Play Games
  • WPF本身不支持直接的3D繪圖,但是它提供了一些用於實現3D效果的高級技術。 如果你想要在WPF中進行3D繪圖,你可以使用兩種主要的方法: WPF 3D:這是一種在WPF應用程式中創建3D圖形的方式。WPF 3D提供了一些基本的3D形狀(如立方體、球體和錐體)以及一些用於控制3D場景和對象的工具(如 ...
  • 一、XML概述 XML(可擴展標記語言)是一種用於描述數據的標記語言,旨在提供一種通用的方式來傳輸和存儲數據,特別是Web應用程式中經常使用的數據。XML並不預定義標記。因此,XML更加靈活,並且可以適用於廣泛的應用領域。 XML文檔由元素(element)、屬性(attribute)和內容(con ...
  • 從今年(2023)三月份開始,Github開始強制用戶開啟兩步驗證2FA(雙因數)登錄驗證,毫無疑問,是出於安全層面的考慮,畢竟Github賬號一旦被盜,所有代碼倉庫都會毀於一旦,關於雙因數登錄的必要性請參見:別讓你的伺服器(vps)淪為肉雞(ssh暴力破解),密鑰驗證、雙向因數登錄值得擁有。 雙因 ...
  • 第一題 下列代碼輸入什麼? public class Test { public static Test t1 = new Test(); { System.out.println("blockA"); } static { System.out.println("blockB"); } publi ...
  • 本文主要涉及的問題:用ElementTree和XPath讀寫XML文件;解決ElementTree新增元素後再寫入格式不統一的問題;QTableWidget單元格設置控制項 ...
  • QStandardItemModel 類作為標準模型,主打“類型通用”,前一篇水文中,老周還沒提到樹形結構的列表,本篇咱們就好好探討一下這貨。 還是老辦法,咱們先做示例,然後再聊知識點。下麵這個例子,使用 QTreeView 組件來顯示數據,使用的列表模型比較簡單,只有一列。 #include <Q ...
  • 一、直充內充(充值方式) 直充: 包裝套餐直接充值到上游API系統。【PID/Smart】 (如:支付寶、微信 話費/流量/語音/簡訊 等 充值系統)。 內充(套餐打包常見物聯卡系統功能): 套餐包裝 適用於不同類型套餐 如 流量、簡訊、語音 等。 (目前已完善流量邏輯) 二、套餐與計費產品 計費產 ...
  • 在前面幾天中,我們學習了Dart基礎語法、可迭代集合,它們是Flutter應用研發的基本功。今天,我們繼續學習Flutter應用另一個必須掌握知識點:非同步編程(即Future和async/await)。它類似於Java中的FutureTask、JavaScript中的Promise。它是後續Flut... ...
  • 針對改動範圍大、影響面廣的需求,我通常會問上線了最壞情況是什麼?應急預案是什麼?你帶開關了嗎?。當然開關也是有成本的,接下來本篇跟大家一起交流下高頻發佈支撐下的功能開關技術理論與實踐結合的點點滴滴。 ...
  • 1.d3.shuffle D3.shuffle() 方法用於將數組中的元素隨機排序。它使用 Fisher–Yates 洗牌演算法,該演算法是無偏的,具有最佳的漸近性能(線性時間和常數記憶體)。 D3.shuffle() 方法的語法如下: d3.shuffle(array, [start, end]) 其中 ...