關鍵詞:委托不可變性 · 多播委托 · 調用列表管理
?? 一、委托的核心特性:不可變性
看似“添加”,實為新建
使用 += 為委托“添加”方法時(如 delVar += SCl.m3;):
- 系統創建全新委托對象
- 新委托的調用列表 = 原列表 + 新增方法
- 原委托對象保持不變(內存地址不變)
? 本質:通過新建實現“修改”,符合委托不可變原則
內存變化圖解
MyDel delVar = inst.MyM1; // 初始委托 (指向方法1)
delVar += SCl.m3; // 新建委托 (方法1+方法2)
delVar += X.Act; // 再新建委托 (方法1+2+3)
🧩 二、安全移除方法的機制
-= 運算符的運作邏輯
delVar -= SCl.m3; // 從調用列表移除方法
- 創建新委托對象,復制除目標方法外的所有引用
- 移除規則:
- 從列表尾部向前搜索
- 僅移除第一個匹配的方法實例
- 試圖移除不存在的方法時靜默忽略
空委托防護
// 正確檢查方式
if (delVar != null)
{delVar(55);
}
// 或使用空條件運算符
delVar?.Invoke(65);
?? 未檢查空委托直接調用將拋出 NullReferenceException
📡 三、委托調用的雙模式與陷阱
關鍵特性:
- 參數傳遞:調用時傳入的參數應用于所有方法
- 重復執行:若同一方法在列表中出現多次,每次都會被調用
- 輸出參數:需特殊處理(避免值覆蓋問題)
💻 四、完整實例解析
delegate void PrintFunction(); // 無返回值委托 class Test
{public void Print1() => Console.WriteLine("Print1 -- instance");public static void Print2() => Console.WriteLine("Print2 -- static");
}class Program
{static void Main(){var t = new Test();PrintFunction pf = t.Print1; // 初始化// 添加三個方法(實際新建兩次委托)pf += Test.Print2; pf += t.Print1; pf += Test.Print2;pf?.Invoke(); // 安全調用 }
}
輸出結果:
Print1 – instance
Print2 – static
Print1 – instance
Print2 – static
🔑 關鍵點總結
- 不可變性是核心:所有“修改”操作實質是創建新委托對象
- 移除順序敏感:從列表尾部開始匹配移除首個目標方法
- 空委托防護:必須使用 if (delVar != null) 或 ?. 運算符
- 調用成本:每次調用遍歷整個調用列表,需注意性能影響
掌握委托的不可變本質,能有效避免異步編程中的常見陷阱。建議在事件處理等場景中始終遵循「添加后必移除」原則,防止內存泄漏問題。