You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

事件聚合器模式与委托/事件对比:实现带命名事件的简易聚合器疑问

嘿,你这个问题其实是没吃透委托的多播特性,完全不用退而求其次用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:02:11