NullReference 错误仅在不在 Unity 3D 播放器中调试时出现

NullReference error only when not debugging in Unity 3D player

我在 Unity 3D 应用程序中工作,其中有 2 种方法可以使用 Oauth 连接到服务:一种方法检索令牌,另一种方法检索服务的 JSON 输出,基于令牌。 JSON 准备就绪后,我将更改 TextMesh 上的文本。 TextMesh 脚本的启动方法中的所有内容 运行ning。像这样:

void Start()
{
    string authToken = getAuthToken();
    CustomerGroups data = getCustGroups(authToken);

    TextMesh curText = (TextMesh)gameObject.GetComponent(typeof(TextMesh));
    curText.text = data.value[0].Field1+ "-" + data.value[0].Field2;
}

奇怪的是,当我在 Visual Studio 中使用 "Attach to Unity" 并且在 Unity 播放器中使用 运行 GameObject 时,这很好用,但是当我 运行 它没有调试时(就像 运行 宁几步)我总是得到:

NullReferenceException: Object reference not set to an instance of an object
HelloWorldInteractive.getAuthToken () (at Assets/HelloWorldInteractive.cs:106)
HelloWorldInteractive.Start () (at Assets/HelloWorldInteractive.cs:15)

实际方法是:

public string getAuthToken()
{
    string token = string.Empty;
    Dictionary<string, string> content = new Dictionary<string, string>();
    TokenClassName json = null;

    content.Add("tenant_id", "https//...");
    content.Add("grant_type", "client_credentials");
    content.Add("client_id", "xxxx");
    content.Add("client_secret", "xxxx");
    content.Add("resource", "https://...");

    UnityWebRequest www = UnityWebRequest.Post("https://login...", content);

    //Send request
    www.SendWebRequest();

    if (!www.isNetworkError)
    {
        string resultContent = www.downloadHandler.text;
        json = JsonUtility.FromJson<TokenClassName>(resultContent);

        //Return result
        token = json.access_token;
    }

    return token;
}

再一次,如果我不调试它,它会失败,但是当我在 运行 调试它时,它工作得很好。我假设它可能与在 Start 方法中执行它们有关……也许我需要在其他地方执行它?我只需要准备好 JSON 数据,这样我就可以在开始时更改 TextMesh 的值。

您的问题称为 "race condition"。

当你在调试时你是 "slow enough" 所以你的 UnityWebRequest 很可能有一个结果,直到你到达需要它的代码部分。

虽然不调试:您不会等待 直到网络请求完成。

所以当方法到达行

时,www.downloadHandler.text; 仍将具有值 null
json = JsonUtility.FromJson<TokenClassName>(resultContent);

我不知道 exctaly JsonUtility.FromJson 对作为输入值的 null 做了什么,但我猜要么错误已经在那里抛出,要么它可能 return null 所以下一行

token = json.access_token;

尝试使用值 null 访问 json 时抛出异常。


你必须使用 Coroutine and yield until you have a result (see UnityWebRequest.Post)。

我会使用像

这样的回调方法
private IEnumerator getAuthToken(Action<string> onSuccess)
{
    string token = string.Empty;
    Dictionary<string, string> content = new Dictionary<string, string>();
    TokenClassName json = null;

    content.Add("tenant_id", "https//...");
    content.Add("grant_type", "client_credentials");
    content.Add("client_id", "xxxx");
    content.Add("client_secret", "xxxx");
    content.Add("resource", "https://...");

    UnityWebRequest www = UnityWebRequest.Post("https://login...", content);

    //Send request
    // !! wait until request finishes
    yield return www.SendWebRequest();

    if (!www.isNetworkError && !www.isHttpError)
    {
        string resultContent = www.downloadHandler.text;
        json = JsonUtility.FromJson<TokenClassName>(resultContent);

        //Return result
        token = json.access_token;

        // this should only be done on success
        // execute the callback
        onSuccess?.Invoke(token);
    }
    else
    {
        Debug.LogErrorFormat(this, "Downlaod failed with: {0} - \"{1}\"", www.responseCode, www.error);
    }
}

并将其与回调方法一起使用,例如

private Start()
{
    StartCoroutine(getAuthToken(OnReceivedToken));
}

privtae void OnReceivedToken(string authToken)
{
    CustomerGroups data = getCustGroups(authToken);

    TextMesh curText = (TextMesh)gameObject.GetComponent(typeof(TextMesh));
    curText.text = data.value[0].Field1+ "-" + data.value[0].Field2;     
}

或作为 lambda 表达式

private Start()
{
    StartCoroutine(getAuthToken(
        (authToken) =>
        {
            CustomerGroups data = getCustGroups(authToken);

            TextMesh curText = (TextMesh)gameObject.GetComponent(typeof(TextMesh));
            curText.text = data.value[0].Field1+ "-" + data.value[0].Field2;
        }
    ));
}