如何创建Action<T>列表?实现类型匹配事件中心遇阻求助
实现基于类型匹配的事件中心方案
你的核心问题在于不同泛型参数的Action<T>是完全不同的委托类型,无法直接存入同一个字典——哪怕用dynamic也不行,因为Action<T>和Action<dynamic>的类型系统不兼容。下面给你一个既满足类型安全、调用性能又几乎无损耗的实现方案:
核心思路
用Action<object>作为字典的Value类型,在注册时将泛型Action<T>包装成Action<object>(内部做类型转换),调用时直接传入对象并执行包装后的委托。这种方式注册时仅做一次委托包装,调用时只有一次轻量的类型转换(引用类型无装箱开销,值类型只有一次装箱),完全符合你"调用无较大性能损耗"的要求。
修改后的完整代码
// 字典改用Action<object>作为值类型 private readonly Dictionary<Type, Action<object>> _functions = new Dictionary<Type, Action<object>>(); public void RegisterHandler<T>(Action<T> handler) where T : BaseClass { var type = typeof(T); // 可选:避免重复注册同一类型的处理器 if (_functions.ContainsKey(type)) throw new InvalidOperationException($"Handler for type {type.Name} is already registered."); // 将Action<T>包装成Action<object>,内部安全转换类型 _functions[type] = obj => handler((T)obj); } public void CallMethod(BaseClass data) { if (_functions.TryGetValue(data.GetType(), out var handler)) { handler.Invoke(data); } else { // 可选:处理未注册类型的情况,比如抛出异常或忽略 throw new KeyNotFoundException($"No handler registered for type {data.GetType().Name}"); } }
为什么这个方案可行?
- 类型安全:注册时通过泛型约束
where T : BaseClass保证传入的T都是BaseClass的子类,所以包装时的(T)obj转换是安全的(不会出现类型转换失败)。 - 性能优异:调用时只是执行一个预先包装好的委托,内部的类型转换是CLR原生支持的操作,性能损耗微乎其微——比反射调用快几个数量级。
- 实现简洁:不需要复杂的反射逻辑,注册时的包装代码非常简单,维护成本低。
可选扩展
如果你需要支持子类触发父类的处理器(比如注册了BaseClass的处理器,传入子类实例也能触发),可以在CallMethod中遍历类型的继承链查找处理器,示例如下:
public void CallMethod(BaseClass data) { var type = data.GetType(); while (type != null && type != typeof(object)) { if (_functions.TryGetValue(type, out var handler)) { handler.Invoke(data); // 可选:找到后是否继续查找父类处理器,按需调整 return; } type = type.BaseType; } throw new KeyNotFoundException($"No handler registered for type {data.GetType().Name} or its base types."); }
内容的提问来源于stack exchange,提问作者Swazimodo
相关产品推荐
相关产品推荐

