Unity 2018.4 运行时指定Type调用泛型AddListener并传入对应泛型委托
解决方案
核心原理
你定义的GameEvent.EventDelegate<in T>使用了逆变泛型参数in T,因此接收BaseEvent参数的方法,完全可以合法适配任意派生自BaseEvent的T对应的EventDelegate<T>委托类型,运行时调用不会出现签名不匹配问题。
实现步骤
- 新增成员变量存储创建的委托实例,保证后续移除监听时使用同一个实例:
// 存储事件类型对应创建的委托实例,用于后续移除监听 private readonly Dictionary<Type, Delegate> _dynamicListeners = new Dictionary<Type, Delegate>();
- 动态添加监听的完整实现:
private Type type = typeof(CustomGameEvent); // 运行时动态获取的事件类型 private void GameEventHandler(GameEvent.BaseEvent e) { // 你的处理逻辑,不需要关心具体派生类型 } private void AddDynamicListener() { // 1. 构造泛型AddListener方法 var addMi = typeof(GameEvent).GetMethod(nameof(GameEvent.AddListener)) ?.MakeGenericMethod(type); if (addMi == null) return; // 2. 构造目标委托类型 var delegateType = typeof(GameEvent.EventDelegate<>).MakeGenericType(type); // 3. 创建目标类型的委托实例,利用逆变特性适配GameEventHandler方法 var targetDelegate = Delegate.CreateDelegate(delegateType, this, nameof(GameEventHandler)); // 4. 存储委托实例,用于后续移除 _dynamicListeners[type] = targetDelegate; // 5. 调用AddListener方法 addMi.Invoke(null, new object[] { targetDelegate }); }
- 动态移除监听的实现:
private void RemoveDynamicListener() { if (!_dynamicListeners.TryGetValue(type, out var targetDelegate)) return; // 1. 构造泛型RemoveListener方法 var removeMi = typeof(GameEvent).GetMethod(nameof(GameEvent.RemoveListener)) ?.MakeGenericMethod(type); if (removeMi == null) return; // 2. 调用移除方法 removeMi.Invoke(null, new object[] { targetDelegate }); // 3. 清理存储 _dynamicListeners.Remove(type); }
常见问题说明
- 为什么之前的
Convert.ChangeType和CastInto方法无效?Convert.ChangeType仅支持实现了IConvertible接口的类型转换,委托类型未实现该接口,所以会抛出异常。直接硬转基类委托到派生类委托不符合CLR类型安全规则,因此也无法生效。 - 运行时调用委托会不会出错?
因为委托声明了in T逆变,CLR会保证传入的派生类事件参数可以安全转换为BaseEvent传入你的处理方法,不会出现类型转换错误,完全符合你只感知事件触发时机的需求。
内容的提问来源于stack exchange,提问作者Michael Ryan
相关产品推荐
相关产品推荐

