委托 是類型安全的函數指針,多播委托 可鏈接多個方法,調用時按添加順序依次執行。所有委托隱式繼承 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 參數會被最後調用者的結果覆蓋) |
| 返回值 | 多播委托返回值為 最後一個方法的返回值(若委托有返回值) |
| 異常處理 | 若某方法拋異常,後續方法不再執行,異常向上冒泡 |
MulticastDelegate.GetInvocationList() 返回 Delegate[],每個元素是一個單播委托,指向鏈中的一個目標方法。可用於:
event 關鍵字封裝委托,防止外部直接賦值 (=) 覆蓋訂閱者。
public event Calculation OnCalculate;
WeakReference 封裝。
BeginInvoke/EndInvoke (APM 模式) 或 Task.Run(() => multi(args)) (現代)。
Interlocked.CompareExchange 更新委托字段。
var copy = i; action += () => Do(copy);多播委托是 C# 事件機制的基石。理解其「鏈式調用、共享參數、最後返回值、異常中斷」特性,配合 GetInvocationList 診斷調用鏈,是掌握 C# 核心編程模型的關鍵。現代開發中更多直接使用 event、Action/Func、Task,但底層原理相通。