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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 14:57:02