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

如何使用UniTaskVoid订阅Unity事件?

解决UniTask在Unity UI事件订阅中替代async void的闭包问题

问题背景

UniTask文档明确建议避免使用async void,转而使用UniTaskVoid处理异步逻辑,但在订阅Unity UI事件(比如Button的onClick)时,直接用UniTaskVoid会出现执行时机错误,可行方案又会触发Rider的"可消除闭包"警告。

错误尝试分析

  1. 直接传入UniTaskVoid方法:_restartGameButton.onClick.AddListener(RestartGame);
    Unity的AddListener需要UnityAction(无参无返回值委托),但UniTaskVoid方法返回值为UniTaskVoid,无法直接适配,编译会报错。

  2. 错误调用RestartGame().Forget:_restartGameButton.onClick.AddListener(RestartGame().Forget);
    这种写法会立即执行RestartGame()方法,而非将其作为事件回调注册,完全不符合预期。

消除闭包的正确实现

核心是避免用lambda表达式捕获上下文对象,将异步逻辑抽成独立的UniTaskVoid成员方法,再通过UniTask.Void的方法组重载适配UnityAction:

public class WindowGamePause : WindowView
{
    [SerializeField] private Button _restartGameButton;
    // 假设已通过其他方式初始化_gameManager
    private GameManager _gameManager;

    private void Awake()
    {
        _restartGameButton.onClick.AddListener(OnRestartButtonClicked);
    }
    
    // 事件回调的同步方法,无闭包
    private void OnRestartButtonClicked()
    {
        // 直接传入异步方法的方法组,避免创建闭包
        UniTask.Void(RestartGameAsync);
    }
    
    // 独立的异步逻辑方法
    private async UniTaskVoid RestartGameAsync()
    {
        await Close();
        _gameManager.RestartGame();
    }
}

原理说明

  • 之前的lambda写法async () => { ... }会捕获当前类实例(this)和_gameManager变量,创建额外闭包对象,触发Rider警告。
  • 使用方法组RestartGameAsync传入UniTask.Void时,直接引用成员方法地址,无需捕获上下文,不会产生闭包,警告自然消除。
  • UniTask.Void的作用是将UniTaskVoid类型的异步逻辑包装成同步无返回值委托,完美适配Unity的UnityAction要求,同时遵循UniTask避免async void的最佳实践。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 01:01:18