You need to enable JavaScript to run this app. 如何编写可返回数据的C#函数?Unity Firebase异步取数问题求解
优惠活动
大模型
产品
解决方案
定价
更多

如何编写可返回数据的C#函数?Unity Firebase异步取数问题求解

现有代码的核心问题
  • 执行时序错位:GetValueAsync是Firebase封装的异步网络操作,方法调用后会立刻返回,不会阻塞等待网络请求完成。当前代码里ContinueWith中的赋值逻辑,要等网络请求返回结果才会执行,执行时机远晚于方法主流程执行print(returnDataValue)和return的步骤,所以你运行时拿到、打印出来的永远是初始值null。
  • 跨线程风险:Firebase默认的异步任务回调运行在后台线程,如果后续要把获取到的值同步到Unity的UI组件、游戏对象上,会直接触发跨线程操作的权限报错。
  • 常见误区澄清:
    • 不是GetReference内部无法返回值,是异步操作的结果天然无法嵌入到同步方法的返回值链路中,受执行时序限制,同步方法根本等不到异步结果返回就已经退出了。
    • 协程并非完全无法传递返回值,只是协程的固定返回类型是IEnumerator,没法直接返回字符串这类业务数据,完全可以通过回调参数、外部变量赋值的方式传递结果,不存在“协程绝对无法返回值”的限制。
可行的改造方案

方案1:用回调委托替代直接返回(最兼容现有逻辑)

这种方式不需要大幅改动原有调用习惯,只需要把拿到结果后的逻辑挪到回调里执行即可,同时通过调度器把回调切回Unity主线程,规避跨线程操作问题。
改造后的代码:

// 新增回调委托,拿到值后触发后续逻辑
public void getData(string DataName, Action<string> onGetDataCompleted)
{
    FirebaseDatabase dbInstance = FirebaseDatabase.DefaultInstance;
    dbInstance.GetReference("users").Child(User.UserId).GetValueAsync().ContinueWith(task => 
    {
        // 优先处理任务异常
        if (task.IsFaulted || task.IsCanceled)
        {
            onGetDataCompleted?.Invoke(null);
            return;
        }
        if (task.IsCompleted) 
        {
            DataSnapshot snapshot = task.Result;
            string returnDataValue = snapshot.Child(DataName).Value?.ToString();
            UnityEngine.Debug.Log(returnDataValue);
            // 把结果传给回调逻辑
            onGetDataCompleted?.Invoke(returnDataValue);
        }
    }, TaskScheduler.FromCurrentSynchronizationContext()); // 该参数会把回调切到创建任务时的主线程上下文
}

调用时不需要等待方法同步返回结果,直接把拿到值后要执行的逻辑写在回调参数中即可,示例:

// 调用示例
getData("nickname", (result) => 
{
    if (!string.IsNullOrEmpty(result))
    {
        // 这里可以直接用result操作主线程对象,比如更新UI、触发游戏逻辑
        Debug.Log($"获取到的用户昵称:{result}");
    }
});

方案2:使用async/await语法(代码更简洁)

如果你的Unity版本支持C# 7.0以上(Unity2018及后续版本基本满足),可以直接通过异步方法拿到返回值,避免回调嵌套。

public async Task<string> getData(string DataName)
{
    FirebaseDatabase dbInstance = FirebaseDatabase.DefaultInstance;
    try
    {
        DataSnapshot snapshot = await dbInstance.GetReference("users").Child(User.UserId).GetValueAsync();
        string returnDataValue = snapshot.Child(DataName).Value?.ToString();
        Debug.Log(returnDataValue);
        return returnDataValue;
    }
    catch (Exception e)
    {
        Debug.LogError($"获取数据失败:{e.Message}");
        return null;
    }
}

注意不要把方法写成async void,否则内部抛出的异常无法被捕获,容易引发程序崩溃。调用示例:

private async void Start()
{
    string userNickname = await getData("nickname");
    Debug.Log($"获取到的用户昵称:{userNickname}");
}

坚持使用协程的处理方式

协程本身没法直接返回字符串,但是可以通过外部变量、回调的方式传递结果,示例代码:

public IEnumerator getDataCoroutine(string DataName, Action<string> onGetCompleted)
{
    string returnDataValue = null;
    bool isTaskCompleted = false;
    FirebaseDatabase dbInstance = FirebaseDatabase.DefaultInstance;
    dbInstance.GetReference("users").Child(User.UserId).GetValueAsync().ContinueWith(task => 
    {
        if (task.IsCompleted) 
        {
            DataSnapshot snapshot = task.Result;
            returnDataValue = snapshot.Child(DataName).Value?.ToString();
        }
        isTaskCompleted = true;
    });
    // 协程轮询等待异步任务完成
    yield return new WaitUntil(() => isTaskCompleted);
    Debug.Log(returnDataValue);
    onGetCompleted?.Invoke(returnDataValue);
}

直接在主线程启动协程即可,不需要额外处理跨线程问题,调用示例:

StartCoroutine(getDataCoroutine("nickname", (result) => 
{
    Debug.Log($"协程获取到的值:{result}");
}));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.31 02:27:01