如何通过传入Action<Guid>移除EventHandler多播委托中的指定订阅?
问题分析
原代码的核心问题在于添加订阅时的包装逻辑导致取消订阅无法匹配:
- 调用
AddOnChanged时,每次都会创建一个新的匿名lambda表达式(_, arg) => eventDelegate(arg)作为EventHandler<Guid>添加到事件中 - 这个匿名lambda的
Method属性和传入的原始Action<Guid>(比如ListenAction1)的Method完全不同,所以UnsubscribeOnChanged里通过del.Method == actionEvent.Method根本找不到要移除的委托,导致取消订阅无效
解决方案
我们需要维护原始Action<Guid>和包装后的EventHandler<Guid>的映射关系,这样取消订阅时可以直接找到对应的委托进行移除:
public class SomeHandler : IDisposable { private event EventHandler<Guid>? _onChangedSync; // 存储原始Action和包装后的EventHandler的映射 private readonly Dictionary<Action<Guid>, EventHandler<Guid>> _delegateMappings = new(); public void AddOnChanged(Action<Guid> eventDelegate) { // 避免同一个Action重复订阅 if (_delegateMappings.ContainsKey(eventDelegate)) return; // 创建包装后的EventHandler EventHandler<Guid> wrappedDelegate = (_, arg) => eventDelegate(arg); _onChangedSync += wrappedDelegate; // 存入映射字典 _delegateMappings[eventDelegate] = wrappedDelegate; } public void UnsubscribeOnChanged(Action<Guid> actionEvent) { // 从映射字典中找到对应的包装委托并移除 if (_delegateMappings.TryGetValue(actionEvent, out var wrappedDelegate)) { _onChangedSync -= wrappedDelegate; _delegateMappings.Remove(actionEvent); } } public void Change(Guid clinicId) { _onChangedSync?.Invoke(this, clinicId); } public void Dispose() { // 直接清空事件和映射字典,简化清理逻辑 _onChangedSync = null; _delegateMappings.Clear(); GC.SuppressFinalize(this); } }
测试验证
修改后的代码运行原单元测试,_callCount会正确等于1,符合预期。
额外说明
- 加入重复订阅判断,避免同一个Action被多次添加到事件调用列表
- Dispose方法简化为直接将事件置null,同时清空映射字典,比遍历移除更高效
- 映射字典以
Action<Guid>作为键,利用引用类型的特性确保同一个Action实例能被精准匹配
内容的提问来源于stack exchange,提问作者Sebastian
相关产品推荐
相关产品推荐

