关键词:委托不可变性 · 多播委托 · 调用列表管理
⚙️ 一、委托的核心特性:不可变性
看似“添加”,实为新建
使用 += 为委托“添加”方法时(如 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) 或 ?. 运算符
- 调用成本:每次调用遍历整个调用列表,需注意性能影响
掌握委托的不可变本质,能有效避免异步编程中的常见陷阱。建议在事件处理等场景中始终遵循「添加后必移除」原则,防止内存泄漏问题。