事件聚合器模式与委托/事件对比:实现带命名事件的简易聚合器疑问
嘿,你这个问题其实是没吃透委托的多播特性,完全不用退而求其次用Dictionary<string, List<object>>这种不够安全的方案,我给你捋清楚怎么正确实现:
为什么你之前的字典方案没生效?
你大概率是直接给字典的Value赋值了(比如dict[name] = callback),这会直接覆盖之前的委托。而.NET的委托天生支持多播——你可以用+=把多个回调组合成一个"委托链",调用这个组合后的委托时,所有订阅的回调都会依次执行。
正确的实现方式(针对你的Action<int>场景)
直接用Dictionary<string, Action<int>>就能完美解决,核心是利用委托的组合特性来添加订阅者:
public class EventHub { // 用字典存储事件名对应的多播委托 private readonly Dictionary<string, Action<int>> _eventMap = new Dictionary<string, Action<int>>(); public void AddListener(string eventName, Action<int> callback) { // 先尝试获取已有的委托链 _eventMap.TryGetValue(eventName, out var existingCallbacks); // 将新回调追加到委托链(空委托也可以直接组合) _eventMap[eventName] = existingCallbacks + callback; } public void Invoke(string eventName, int message) { // 取出委托链,安全调用(避免委托为null时抛出异常) _eventMap.TryGetValue(eventName, out var callbacks); callbacks?.Invoke(message); } // 可选:添加移除订阅的方法 public void RemoveListener(string eventName, Action<int> callback) { if (_eventMap.TryGetValue(eventName, out var existingCallbacks)) { // 从委托链中移除指定回调 existingCallbacks -= callback; // 如果移除后委托链为空,清理字典中的空条目 if (existingCallbacks == null) { _eventMap.Remove(eventName); } else { _eventMap[eventName] = existingCallbacks; } } } }
要不要用Dictionary<string, List<object>>?
绝对不推荐!这种做法会彻底丢失类型安全:
- 你每次取出元素都要强制转换成
Action<int>,很容易因为类型不匹配抛出异常; - 代码可读性极差,后续维护时很难理清事件和回调的对应关系;
- 完全浪费了委托多播特性带来的简洁性和安全性。
扩展:支持任意参数类型的泛版事件聚合器
如果你的场景需要支持不同参数类型的事件,可以用泛型+Delegate做更灵活的实现:
public class GenericEventHub { private readonly Dictionary<string, Delegate> _eventMap = new Dictionary<string, Delegate>(); public void AddListener<T>(string eventName, Action<T> callback) { if (_eventMap.TryGetValue(eventName, out var existingDelegate)) { _eventMap[eventName] = Delegate.Combine(existingDelegate, callback); } else { _eventMap[eventName] = callback; } } public void Invoke<T>(string eventName, T message) { if (_eventMap.TryGetValue(eventName, out var existingDelegate)) { (existingDelegate as Action<T>)?.Invoke(message); } } public void RemoveListener<T>(string eventName, Action<T> callback) { if (_eventMap.TryGetValue(eventName, out var existingDelegate)) { var newDelegate = Delegate.Remove(existingDelegate, callback); if (newDelegate == null) { _eventMap.Remove(eventName); } else { _eventMap[eventName] = newDelegate; } } } }
内容的提问来源于stack exchange,提问作者bitinn
相关产品推荐
相关产品推荐

