如何编写可返回数据的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
相关产品推荐
相关产品推荐

