C# self==null(在使用可观察集合的脚本中)

C# self==null(在使用可观察集合的脚本中),c#,unity3d,C#,Unity3d,我一直在尝试解决BoostInventory脚本引发的以下错误,每当我在unity项目中尝试向inactiveBoosts ObservableCollection添加增强时: MissingReferenceException:类型为“BoostInventory”的对象已被销毁,但您仍在尝试访问它。 您的脚本应该检查它是否为null,或者不应该销毁对象。 BoostInventory.InactiveBoosts\u CollectionChanged(System.Object sende

我一直在尝试解决BoostInventory脚本引发的以下错误,每当我在unity项目中尝试向inactiveBoosts ObservableCollection添加增强时:

MissingReferenceException:类型为“BoostInventory”的对象已被销毁,但您仍在尝试访问它。 您的脚本应该检查它是否为null,或者不应该销毁对象。 BoostInventory.InactiveBoosts\u CollectionChanged(System.Object sender,System.Collections.Specialized.notifycollectionchangedventargs e)(位于Assets/Scripts/BoostInventory.cs:38) System.Collections.ObjectModel.ObservableCollection`1[T].OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)(位于:0) […续]

boostInventory脚本管理非活动boost清单的显示。更改inactiveBoosts列表时,应删除当前显示的所有增强,并在正确位置重新绘制inactiveBoosts图标。但是,当购买应添加到非活动增强中的增强引发此错误时,不会显示和添加增强。 以下错误将我带到inactiveBoosts ObservableCollection的CollectionChanged函数中的
foreach
循环:

private void InactiveBoosts_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        //clear currently displayed boosts
        foreach (Transform child in transform)
        {
            Destroy(child.gameObject);
        }

        //calculate the number of rows requred to display all the inactive boosts
        float nrows = Convert.ToSingle(Math.Ceiling(gameManager.inactiveBoosts.Count / Convert.ToSingle(ncols)));
        //set the size of the collecting to the right number of rows
        rectTransform.sizeDelta = new Vector2(colWidth * ncols, nrows * colHeight);

        if (gameManager.inactiveBoosts.Count > 0)
        {
            for (int i = 0; i < gameManager.inactiveBoosts.Count; i++)
            {
                Boost currentBoost = gameManager.inactiveBoosts[i];

                int xi = i % ncols;
                int yi = Convert.ToInt32(Math.Floor(i / Convert.ToSingle(ncols)));

                float xpos = (xi * colWidth) + (colWidth / 2) - (colWidth * ncols / 2);
                float ypos = -(yi * colHeight) - (colHeight / 2) + (colHeight * nrows / 2);

                GameObject newChild = Instantiate(boostPanelPrefab, transform, false);
                newChild.transform.localPosition = new Vector2(xpos, ypos);

                //set the boost of the icon to be the current inactive boost
                newChild.GetComponent<InacitveBoost>().boost = currentBoost;
                //set the image of the boost to match the affected building
                newChild.transform.Find("BuildingIcon").GetComponent<UnityEngine.UI.Image>().sprite = boostDisplay.buildingIconDict[currentBoost.affectedBuildings];
                //set the time written on the boost
                newChild.transform.Find("TimeText").GetComponent<TextMeshProUGUI>().text = NumberStringFormatters.SecondsToMinutesAndSeconds(currentBoost.boostDuration);
                //set the multiplier written on the boost
                newChild.transform.Find("MultiplierText").GetComponent<TextMeshProUGUI>().text = "×" + currentBoost.boostMultiplier.ToString("F1");
            }
            noBoostIcon.SetActive(false);
        }
        else
        {
            noBoostIcon.SetActive(true);
        }
    }
由于该错误,我决定尝试将所有的
InactiveBoosts\u CollectionChanged
代码放入
if(self==null)
中。这将删除错误,但现在当然会跳过相应的代码。我试图研究self如何等于null(如果self==null,那么脚本将无法抛出错误,因为它不存在?)。我不太理解这一页上的内容,但我确实注意到了上面的部分

几乎可以肯定的是,您有一个同步错误,两个线程试图同时添加到队列中。也许两个线程都在增加数组中的索引,然后将它们的值放入同一位置

我想这可能意味着我有问题,因为我试图一次将多个项目添加到列表中,但在仅向列表添加一个提升时也会抛出错误

让我更加困惑的是,这个错误只出现在第一次加载游戏时。如果我保存游戏并重新加载,那么一切正常。保存时,inactiveBoosts ObservableCollection转换为列表并由unity序列化,然后在加载时从保存的列表创建新的可观察集合。我看不出这是如何防止错误发生的,但确实如此


这是我在这里的第一个问题,所以如果我没有提供足够的细节(或者已经讲了太久!),请道歉。只要问问你是否需要更多的信息。

是的,这是统一的常见问题,会导致很多混乱

简而言之:
Unity为UnityEngine.Object基类实现了一个自定义“==”运算符,当与null进行比较时,它会执行一些额外的检查。这是一种解决继承托管/本机对象差异的简洁方法

您可以从中获得更多信息

那么他们为什么要这么做呢?博客帖子上的第一个原因实际上是糟糕的。第二个原因很重要。从UnyTycMy.Objor派生的所有对象(包括GAMEObjor、MyStand、Mead、Mesh、……类)在引擎核心的C++侧都有一个本机代码计数器部分。 托管类只是一些本机资源周围的包装器对象。问题产生于这样一个事实,即本机对象可以从托管对象外部销毁。可以通过在该对象上使用Destroy,也可以通过加载新场景隐式地使用Destroy。由于无法手动销毁托管对象,因此它们将一直存在,直到您清除对它们的所有引用,以便最终将其垃圾收集。每当这样的对象失去其本机部分时,该对象就会简单地“假装”它为空,因为您不能使用依赖本机部分的对象中的任何内容

他们在博客中提到,从长远来看,这并不是一个好的决定,因为它在一些情况下会破裂。然而,由于从一开始就是这样,他们不会很快改变它

自定义==运算符的真正问题是运算符不是虚拟方法。因此,编译器将在编译时决定使用哪个运算符。这意味着您可以引用一个死亡游戏对象,该对象的行为会根据存储在其中的变量类型而有所不同

GameObject go = new GameObject("Temp");
DestroyImmediate(go);

if (go == null)  // this is true

object obj = go;
if (obj == null)  // this is false
因此,在使用接口或任何整洁的C#运算符(如、或任何其他直接检查引用是否为null的方法)时,请注意


因此,要解决MissingReferenceException的问题,可能有几个原因导致您的对象可能已被销毁/不再存在。首先,它可能在某处被摧毁。第二个原因可能是它从未“活着”。当您使用“new”创建单行为时,可能会发生这种情况。组件只能存在于游戏对象上,并且它们只能通过使用AddComponent或在克隆整个游戏对象时隐式创建(使用实例化)。常见的错误是,您可能引用了过时的对象,或者只是引用了错误的对象。另一个原因可能是您正在使用Find方法或GetComponent调用,而它实际上无法找到您要查找的内容。在这种情况下(仅当在编辑器中进行测试时),将返回一个伪null对象,该对象应被视为“null”。

感谢您的深入回答,这对于self如何可以为null都是有意义的,因此非常感谢您。关于游戏对象的破坏。我根本不在游戏对象上调用Destroy。脚本附加到场景中的UI游戏对象,因此它应该在游戏启动时出现。脚本实际上完成了所有的操作
GameObject go = new GameObject("Temp");
DestroyImmediate(go);

if (go == null)  // this is true

object obj = go;
if (obj == null)  // this is false