委托链本质是多播委托,通过+=/-=操作符调用Delegate.Combine/Remove实现方法追加与移除;仅void返回值委托适合链式调用,非void仅返回最后一个结果;匿名函数因实例不同导致移除失败;异常会中断后续执行,需手动遍历并捕获。
委托链本质是多播委托
在 C# 中,用
给委托变量添加方法,不是简单地覆盖,而是把新方法追加到内部调用列表里——这个结构叫「多播委托」(MulticastDele
gate
)。只要委托类型签名一致,就能链式叠加。但要注意:
返回值的委托才能安全组成链;如果委托返回非
类型,调用链时只会拿到最后一个方法的返回值,前面的全被丢弃。
+= 和 -= 实际调用 Dele
gate.
Combine 和 Delegate.Remove
编译器把
编译成
,
编译成
。这意味着:
不会重复添加相同方法实例(基于引用相等判断)
只移除第一个匹配的方法实例,不是全部
如果尝试移除一个从未添加过的方法,不会报错,只是静默忽略
委托链为空时,变量值为
,直接调用会抛
常见陷阱:匿名函数和闭包导致移除失败
下面这段代码看似能成功移除,实则无效:
原因:每次写
都创建了新的委托实例,内存地址不同。
找不到原对象。正确做法是保留对要移除方法的引用:
调用委托链时异常会中断后续执行
委托链是顺序同步执行的。一旦某个方法抛出未捕获异常,后面的委托将完全跳过。没有内置的“继续执行下一个”的机制。若需容错,必须手动遍历
并 try/catch 每一项:
这也意味着,别依赖委托链做关键路径的串行任务协调——它不是工作队列,也没有事务性。
委托链看起来简洁,但它的生命周期管理、异常传播、引用一致性都容易出隐性问题。真要构建事件系统或回调调度,优先考虑
关键字封装,或直接用
显式控制。
+=voidvoid+=Delegate.Combine-=Delegate.Remove+=-=nullNullReferenceExceptionAction action = () => Console.WriteLine("A");
action += () => Console.WriteLine("B");
action -= () => Console.WriteLine("B"); // ❌ 不起作用!() => ...Delegate.RemoveAction b = () => Console.WriteLine("B");
action += b;
action -= b; // ✅ 成功GetInvocationList()foreach (Action a in action.GetInvocationList())
{
try { a(); }
catch { /* 忽略或记录 */ }
}eventSystem.Collections.Generic.List