Unity中如何为支持可变参数的事件系统订阅不同参数的Action方法
问题原因
你当前实现的UIEventTriggerManager中,Subscribe方法要求传入的委托类型为Action<object[]>,即接收一个object数组作为唯一参数的方法。而你定义的ChagedCharacter系列方法的参数列表(无参、单int参数、三个参数)和该委托类型不匹配,因此会触发编译错误。
解决方案
方案1:快速适配,无需修改原有方法签名
不需要给所有订阅方法加params object[] args,你可以在订阅时用Lambda表达式包装目标方法,手动完成参数的解析和传递:
public void FirstOpen() { // 无参方法直接调用,忽略传入的args即可 UIEventTriggerManager.Instance.Subscribe("ChagedCharacter", args => ChagedCharacter()); // 单参数方法从args中取出对应位置的参数做类型转换 UIEventTriggerManager.Instance.Subscribe("ChagedCharacter2", args => ChagedCharacter2((int)args[0])); // 多参数方法依次取出对应参数转换后传入 UIEventTriggerManager.Instance.Subscribe("ChagedCharacter3", args => ChagedCharacter3((int)args[0], (string)args[1], (float)args[2])); }
注意:该方案需要保证
Publish事件时传入的参数顺序、类型、数量和订阅时的解析逻辑完全一致,否则会出现运行时类型转换错误、索引越界等问题。
方案2:重构为强类型事件管理器(更推荐)
如果要长期维护这套事件系统,更建议改为泛型实现的强类型事件管理器,编译期即可校验参数匹配性,避免运行时错误,也没有装箱拆箱的性能损耗。
改造后的事件管理器代码:
class UIEventTriggerManager : MonoBehaviour { public static UIEventTriggerManager Instance; // 用事件类型作为key存储对应委托 private Dictionary<Type, Delegate> m_eventHandler = new Dictionary<Type, Delegate>(); private void Awake() { Instance = this; } #region 事件发布重载,可根据需要扩展支持更多参数数量 // 无参数事件发布 public void Publish<TEvent>() where TEvent : struct { if (m_eventHandler.TryGetValue(typeof(TEvent), out var del) && del is Action action) { action?.Invoke(); } } // 单参数事件发布 public void Publish<TEvent, T1>(T1 arg1) where TEvent : struct { if (m_eventHandler.TryGetValue(typeof(TEvent), out var del) && del is Action<T1> action) { action?.Invoke(arg1); } } // 三参数事件发布 public void Publish<TEvent, T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3) where TEvent : struct { if (m_eventHandler.TryGetValue(typeof(TEvent), out var del) && del is Action<T1, T2, T3> action) { action?.Invoke(arg1, arg2, arg3); } } #endregion #region 事件订阅重载,和发布逻辑一一对应 // 无参数事件订阅 public void Subscribe<TEvent>(Action action) where TEvent : struct { var eventType = typeof(TEvent); if (!m_eventHandler.ContainsKey(eventType)) m_eventHandler[eventType] = action; else m_eventHandler[eventType] = Delegate.Combine(m_eventHandler[eventType], action); } // 单参数事件订阅 public void Subscribe<TEvent, T1>(Action<T1> action) where TEvent : struct { var eventType = typeof(TEvent); if (!m_eventHandler.ContainsKey(eventType)) m_eventHandler[eventType] = action; else m_eventHandler[eventType] = Delegate.Combine(m_eventHandler[eventType], action); } // 三参数事件订阅 public void Subscribe<TEvent, T1, T2, T3>(Action<T1, T2, T3> action) where TEvent : struct { var eventType = typeof(TEvent); if (!m_eventHandler.ContainsKey(eventType)) m_eventHandler[eventType] = action; else m_eventHandler[eventType] = Delegate.Combine(m_eventHandler[eventType], action); } #endregion // 取消订阅逻辑同理,对应添加重载即可,核心为Delegate.Remove }
使用方式:
首先定义空struct作为事件的唯一标识,避免字符串拼写错误:
// 定义三个事件的唯一标识 public struct ChagedCharacterEvent { } public struct ChagedCharacter2Event { } public struct ChagedCharacter3Event { }
订阅时直接传入原有方法即可,无需修改方法签名:
public void FirstOpen() { UIEventTriggerManager.Instance.Subscribe<ChagedCharacterEvent>(ChagedCharacter); UIEventTriggerManager.Instance.Subscribe<ChagedCharacter2Event, int>(ChagedCharacter2); UIEventTriggerManager.Instance.Subscribe<ChagedCharacter3Event, int, string, float>(ChagedCharacter3); }
发布事件时直接传对应参数即可:
// 发布无参事件 UIEventTriggerManager.Instance.Publish<ChagedCharacterEvent>(); // 发布单int参数事件 UIEventTriggerManager.Instance.Publish<ChagedCharacter2Event, int>(1001); // 发布多参数事件 UIEventTriggerManager.Instance.Publish<ChagedCharacter3Event, int, string, float>(1002, "测试角色", 99.9f);
注意事项
- 所有订阅的事件,在对应对象销毁/不再需要时一定要调用取消订阅逻辑,避免委托持有对象引用导致内存泄漏。
- 强类型方案最多扩展到4-5个参数的重载即可覆盖绝大多数业务场景,如果有更多参数的需求可以用自定义参数类封装。
内容的提问来源于stack exchange,提问作者KeiPro
相关产品推荐
相关产品推荐

