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

使用UnityAction或C# Action时最优避免内存泄漏及简化注册的方法

问题原因

你封装失败的核心原因有两点:

  • C#中的委托属于值类型,直接将UnityAction作为值参数传入函数时,函数内拿到的是原事件委托的副本,对副本执行+=操作不会修改外部的原始事件MyEvents.SomeEvent,注册自然不会生效
  • 代码存在笔误:参数定义名为_thingToAdd,但注销逻辑中使用了不存在的变量_delegateToAdd,本身就会触发编译错误

另外你提到的HandleSomeEvent本质是方法组,C#会自动将其隐式转换为匹配签名的UnityAction委托实例,属于持有方法引用的委托对象,不是字符串或其他类型。


可行解决方案

方案1:通用全兼容方案(推荐)

如果你的事件是用标准event关键字修饰的(符合C#事件规范),可以用lambda包裹注册/注销逻辑的方式实现封装,不需要修改原有事件定义,适配所有委托类型:

// 直接用C#内置的Action存所有注销逻辑,不需要自定义Dreg委托
private Action deRegisterAll;

// 通用注册方法,分别传入注册逻辑和注销逻辑
public void RegisterEvent(Action registerLogic, Action unregisterLogic)
{
    registerLogic.Invoke();
    deRegisterAll += unregisterLogic;
}

调用时一行完成注册:

// 调用示例,无额外学习成本
RegisterEvent(
    () => MyEvents.SomeEvent += HandleSomeEvent,
    () => MyEvents.SomeEvent -= HandleSomeEvent
);

在脚本销毁/程序退出时统一注销:

private void OnDestroy()
{
    deRegisterAll?.Invoke();
    deRegisterAll = null; // 清空引用避免残留
}

该方案优势:

  • 兼容无参数UnityAction、带参数UnityAction<T>、普通C#Action、标准event等所有委托类型,不需要写多个重载
  • 无反射开销,性能和你手写的原始方案完全一致

方案2:ref参数简化调用

如果你的事件没有加event关键字修饰(直接定义为public static UnityAction SomeEvent),可以用ref参数实现更简洁的调用:

private Action deRegisterAll;

// 无参数UnityAction重载
public void RegisterEvent(ref UnityAction targetEvent, UnityAction handler)
{
    targetEvent += handler;
    deRegisterAll += () => targetEvent -= handler;
}

// 带1个参数的UnityAction<T>重载,有更多参数需求可以继续加重载
public void RegisterEvent<T>(ref UnityAction<T> targetEvent, UnityAction<T> handler)
{
    targetEvent += handler;
    deRegisterAll += () => targetEvent -= handler;
}

调用方式:

RegisterEvent(ref MyEvents.SomeEvent, HandleSomeEvent);

内容的提问来源于stack exchange,提问作者Rachmaninow

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.16 14:06:07