多播委托赋值必须用+=而非=,否则覆盖原有方法;调用前需用?.判空;移除时方法引用须完全一致;异常会中断后续调用,需手动遍历+try/catch处理。

多播委托赋值时别用 =,要用 +=
直接用 = 赋值会覆盖原有委托,不是追加。C# 的多播委托本质是链表结构,只有用 += 才能真正“合并”多个方法到同一个委托实例中。
常见错误现象:myDelegate = MethodA; myDelegate = MethodB; 最终只调用 MethodB —— 因为第二次赋值彻底替换了委托对象。
正确做法:
Action handler = null; handler += MethodA; handler += MethodB; handler(); // 依次调用 MethodA → MethodB
-
+=实际调用Delegate.Combine,自动处理空值和类型检查 - 如果左边是
null,+=会安全初始化,不用提前判空 - 不推荐手动调用
Delegate.Combine(a, b),易出错且不可读
调用多播委托前必须判空,否则 NullReferenceException
即使你刚用 += 加了方法,也不能保证 handler 非空:可能所有订阅者都已用 -= 移除,或初始就是 null。
错误写法:handler(); —— 运行时抛 NullReferenceException。
安全写法(推荐):
以 CLI 为先的表单构建器,采用 BYOK 理念。通过JSON Schema 生成美观的 HTML 表单,使用您自己的 SMTP 服务器处理提交,存储响应...
handler?.Invoke();
-
?.是 C# 6.0+ 的空条件操作符,简洁且线程安全(比if (handler != null) handler();更可靠) - 不要用
handler?.Invoke()+try/catch包裹——异常是设计缺陷,不是流程分支 - 注意:若委托返回值(如
Func),?.会返回null或默认值,需按需处理
移除委托必须用 -=,且方法引用要完全一致
-= 不是“模糊匹配”,而是严格比较方法指针(包括闭包捕获的变量)。稍有不同就移除失败,导致内存泄漏或重复执行。
典型陷阱:
handler += () => Console.WriteLine("A");
handler -= () => Console.WriteLine("A"); // ❌ 失败!这是另一个匿名方法实例
- 命名方法可安全移除:
handler += MethodA; handler -= MethodA; - Lambda 要复用:定义一次再引用,
var lambda = () => ...; handler += lambda; handler -= lambda; - 使用
Action或Func时,不要在循环里反复创建新委托实例再移除
多播委托异常会中断后续调用,无法自动跳过
这是最容易被忽略的设计事实:一旦某个订阅方法抛异常,整个链停止执行,后面的方法永远不会被调用。
例如:
handler += () => { Console.Write("1"); };
handler += () => { throw new Exception(); };
handler += () => { Console.Write("3"); }; // ❌ 永远不会执行
- 没有内置的“容错遍历”机制,
GetInvocationList()+ 手动 try/catch 是唯一可控方式 - 若业务要求强健性,必须显式遍历:
foreach (var d in handler.GetInvocationList()) { try { d.DynamicInvoke(); } catch { /* 忽略或记录 */ } } - 性能影响:反射调用
DynamicInvoke比直接Invoke慢一个数量级,高频场景慎用
多播委托不是“语法糖”,它的调用顺序、异常传播、生命周期管理全由底层 Delegate 类控制。写的时候少想“链式”,多想“事件队列”——每个环节都得自己兜底。









