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

C# 多播委托 完整示例與原理解析

     編輯:關於C語言

委托與多播委托基礎

委托 是類型安全的函數指針,多播委托 可鏈接多個方法,調用時按添加順序依次執行。所有委托隱式繼承 MulticastDelegate,再繼承 Delegate。

核心代碼示例


using System;

namespace DelegateDemo
{
    // 1. 聲明委托類型:兩個輸入參數,一個 ref 輸出參數
    public delegate void Calculation(decimal val1, decimal val2, ref decimal result);

    class MultiCastDemo
    {
        // 實例委托字段
        Calculation _add, _sub, _mul;

        // 三個匹配簽名的方法
        public void Add(decimal a, decimal b, ref decimal r) 
        { r = a + b; Console.WriteLine($"{a} + {b} = {r}"); }
        
        public void Sub(decimal a, decimal b, ref decimal r) 
        { r = a - b; Console.WriteLine($"{a} - {b} = {r}"); }
        
        public void Mul(decimal a, decimal b, ref decimal r) 
        { r = a * b; Console.WriteLine($"{a} * {b} = {r}"); }

        static void Main()
        {
            decimal result = 0m;
            var demo = new MultiCastDemo();
            
            // 2. 綁定單播委托
            demo._add = new Calculation(demo.Add);
            demo._sub = new Calculation(demo.Sub);
            demo._mul = new Calculation(demo.Mul);
            
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine("=== 單獨調用 ===");
            demo._add(7.43m, 5.19m, ref result);
            demo._sub(7.43m, 5.19m, ref result);
            demo._mul(7.43m, 5.19m, ref result);
            
            // 3. 組合為多播委托 (+= 合併,-= 移除)
            Calculation multi = demo._add + demo._sub + demo._mul;
            
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("\n=== 多播調用 (三個方法依次執行) ===");
            multi(7.43m, 5.19m, ref result); // result 最終為乘法結果
            
            Console.ForegroundColor = ConsoleColor.Magenta;
            Console.WriteLine("\n=== 移除 Sub 後調用 ===");
            multi -= demo._sub;
            multi(7.43m, 5.19m, ref result); // 只執行 Add 和 Mul
            
            // 4. 恢復 Sub
            multi += demo._sub;
            
            // 5. 遍歷調用列表
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("\n=== 當前調用鏈包含的方法 ===");
            foreach (Delegate d in multi.GetInvocationList())
            {
                Console.WriteLine(d.Method.Name); // 輸出: Add, Sub, Mul
            }
            
            Console.ResetColor();
        }
    }
}

關鍵行為解析

操作說明
delegate + delegate返回新委托,調用列表為左操作數列表接右操作數列表
delegate - delegate從左操作數列表中移除右操作數列表(末尾匹配),返回新委托
調用時參數所有方法共享同一參數引用(ref 參數會被最後調用者的結果覆蓋)
返回值多播委托返回值為 最後一個方法的返回值(若委托有返回值)
異常處理若某方法拋異常,後續方法不再執行,異常向上冒泡

內部原理:GetInvocationList

MulticastDelegate.GetInvocationList() 返回 Delegate[],每個元素是一個單播委托,指向鏈中的一個目標方法。可用於:

  • 檢查調用鏈長度
  • 手動逐個調用並捕獲單獨異常(防止一個失敗影響其他)
  • 序列化/檢查委托內容

實戰最佳實踐

  1. 事件模式:公開事件用 event 關鍵字封裝委托,防止外部直接賦值 (=) 覆蓋訂閱者。
    public event Calculation OnCalculate;
  2. 弱引用事件:長生命周期發布者持有短生命周期訂閱者時,防止內存洩漏,可用 WeakReference 封裝。
  3. 異步調用:BeginInvoke/EndInvoke (APM 模式) 或 Task.Run(() => multi(args)) (現代)。
  4. 線程安全:多線程添加/移除處理器時,需鎖定或使用 Interlocked.CompareExchange 更新委托字段。

常見陷阱

  • 閉包陷阱:在循環中用 lambda 訂閱事件,捕獲循環變量導致所有處理器見到相同最終值。解決:var copy = i; action += () => Do(copy);
  • 忘記取消訂閱:導致對象無法被 GC 回收(內存洩漏)。
  • ref/out 參數共享:多播鏈中所有方法共享同一 ref 變量,最終值以最後執行者為準,邏輯上易誤解。

總結

多播委托是 C# 事件機制的基石。理解其「鏈式調用、共享參數、最後返回值、異常中斷」特性,配合 GetInvocationList 診斷調用鏈,是掌握 C# 核心編程模型的關鍵。現代開發中更多直接使用 event、Action/Func、Task,但底層原理相通。

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