泛型数量可变的自定义事件系统:如何设计通用基类?
我实现了一个可同时触发本地事件与网络事件的自定义事件系统,该系统基于Action,需支持最多16个泛型参数(如Event、Event<T>、Event<T1,T2>等)。当前采用硬编码所有可能泛型类的方式实现(代码示例如下),但希望通过基类或接口统一管理这些类,却因泛型参数数量不同,无法确定基类/接口的设计方式。请问是否有可行方案?还是只能保持当前硬编码的实现方式?
public class Event { private Action eventAction = delegate { }; public void Subscribe(Action function) => eventAction += function; public void Unsubscribe(Action function) => eventAction -= function; public void Invoke(bool invokeNetworkEvent = false) { // 触发本地事件 eventAction?.Invoke(); if (!invokeNetworkEvent) return; // 触发网络事件 } } public class Event<T1> { private Action<T1> eventAction = delegate(T1 _obj1) { }; public void Subscribe(Action<T1> function) => eventAction += function; public void Unsubscribe(Action<T1> function) => eventAction -= function; public void Invoke(T1 _param1, bool invokeNetworkEvent = false) { // 触发本地事件 eventAction?.Invoke(_param1); if (!invokeNetworkEvent) return; // 触发网络事件 } } public class Event<T1, T2> { private Action<T1, T2> eventAction = delegate(T1 _obj1, T2 _obj2) { }; public void Subscribe(Action<T1, T2> function) => eventAction += function; public void Unsubscribe(Action<T1, T2> function) => eventAction -= function; public void Invoke(T1 _param1, T2 _param2, bool invokeNetworkEvent = false) { // 触发本地事件 eventAction?.Invoke(_param1, _param2); if (!invokeNetworkEvent) return; // 触发网络事件 } }
1. 非泛型基类复用核心逻辑
提取所有Event类的共性(比如网络事件触发逻辑),定义一个抽象基类,让所有泛型Event类继承它,避免重复代码:
public abstract class BaseEvent { // 统一实现网络事件触发逻辑,所有子类复用 protected void InvokeNetwork() { // 网络事件触发的通用代码 } // 暴露给子类的本地事件触发抽象方法 protected abstract void InvokeLocal(); // 统一的对外Invoke入口 public void Trigger(bool invokeNetworkEvent = false) { InvokeLocal(); if (invokeNetworkEvent) { InvokeNetwork(); } } }
然后每个泛型Event类继承BaseEvent,实现InvokeLocal并处理自身的泛型逻辑:
public class Event : BaseEvent { private Action _eventAction = delegate { }; public void Subscribe(Action function) => _eventAction += function; public void Unsubscribe(Action function) => _eventAction -= function; protected override void InvokeLocal() { _eventAction?.Invoke(); } // 保持原有调用习惯的重载 public void Invoke(bool invokeNetworkEvent = false) { Trigger(invokeNetworkEvent); } } public class Event<T1> : BaseEvent { private Action<T1> _eventAction = delegate(T1 _) { }; private T1 _currentParam; public void Subscribe(Action<T1> function) => _eventAction += function; public void Unsubscribe(Action<T1> function) => _eventAction -= function; protected override void InvokeLocal() { _eventAction?.Invoke(_currentParam); } public void Invoke(T1 param1, bool invokeNetworkEvent = false) { _currentParam = param1; Trigger(invokeNetworkEvent); } }
这种方式能复用网络触发逻辑,同时让所有Event类统一继承自BaseEvent,方便后续统一管理(比如存入集合、批量操作)。
2. 代码生成器自动生成泛型类
由于最多需要16个泛型参数,手动编写所有类过于繁琐,可以使用T4模板或Roslyn代码生成器自动生成从0到16个泛型参数的Event类。
比如用T4模板循环生成不同泛型版本的类,替换参数数量、泛型类型列表、Action类型等重复部分,一次生成所有需要的代码,既避免手动硬编码的工作量,又能保持强类型特性。
3. 动态参数封装(不推荐强类型场景)
如果可以接受弱类型,可以用object[]传递参数,定义单个非泛型Event类:
public class DynamicEvent { private Action<object[]> _eventAction = delegate { }; public void Subscribe(Action<object[]> function) => _eventAction += function; public void Unsubscribe(Action<object[]> function) => _eventAction -= function; public void Invoke(bool invokeNetworkEvent = false, params object[] args) { _eventAction?.Invoke(args); if (invokeNetworkEvent) { // 网络事件触发逻辑,传递args } } }
但这种方式会丢失强类型检查,调用时需要手动装箱拆箱,仅适合对类型要求较低的场景,不符合你基于Action的强类型系统需求,因此不推荐。
如果要保留强类型特性,最优方案是非泛型基类复用核心逻辑 + 代码生成器自动生成泛型类,既避免手动硬编码的繁琐,又能保证类型安全。如果不想引入代码生成工具,用基类复用逻辑也能大幅减少重复代码,比纯硬编码更优。
内容的提问来源于stack exchange,提问作者Meister der Magie

