Unity/C# 空引用异常不存在,但应该存在

Unity/C# null reference exception isn't present, but should be

程序落到这个分支了还没有暗恋,很意外。对象被销毁并为空,但所有数据仍然相同。据我了解,它应该因空引用异常而崩溃。我是否遗漏了一些关于 null 在 c# 中如何工作的一般信息,或者我在 unity/dotnet 中遇到了一些已知错误?

if (begining == null)
{
    print(begining.marked);
    foreach (var near in begining.nears)
    {
        print(near);
    }
    print(begining!.distance);
    print(begining.nears_amount);
    print(begining.prev);
}

开头是节点类型的对象:

public class Node : MonoBehaviour
{
    public int nears_amount = 3;
    public readonly HashSet<Node> nears = new HashSet<Node>();
    public float distance = Single.PositiveInfinity;
    public Node? prev = null;
    public bool marked = false;
    public void connect(Node another);
    private void OnDrawGizmos();
    public void disconnect_all();
    private void OnMouseOver();
    public static bool BFS(Node start, Node seeked);
    public static List<Node> Dijkstra(Node start, int depth = -1);
    public static List<Node> DFS(Node current, int depth);
}

调试器:

长话短说:一般来说,对于从 UnityEngine.Object 继承的事物,在检查是否存在

时宁愿使用 bool operator
if (begining)
{
    print(begining.marked);
    foreach (var near in begining.nears)
    {
        print(near);
    }
    print(begining.distance);
    print(begining.nears_amount);
    print(begining.prev);
}

请注意,对于从 UnityEngine.Object.

继承的任何内容,Unity 都有一个 Custom == implementation (see also further explanation here)

这个习俗==主要是基于

static bool CompareBaseObjects(UnityEngine.Object lhs, UnityEngine.Object rhs)
{
    bool lhsNull = ((object)lhs) == null;
    bool rhsNull = ((object)rhs) == null;

    if (rhsNull && lhsNull) return true;

    if (rhsNull) return !IsNativeObjectAlive(lhs);
    if (lhsNull) return !IsNativeObjectAlive(rhs);

    return lhs.m_InstanceID == rhs.m_InstanceID;
}

虽然基础 System.Object (= object) 实际上可能 而不是 c# 层上 null,这custom == 额外检查 c++ 层上的底层实例是否存在或被销毁 (IsNativeObjectAlive).

请注意 Unity 如何不显示 NullReferenceException 而是显示自定义 MissingReferenceException,表明此 System.Object 实际上不是真正的 null,而是 Unity 运算符== returns true 对于 null,即使 System.Object 仍然存在。

一旦您销毁了一个对象,然后尝试在同一帧中访问它(或者最好在 GC 收集该实例并使这个 System.Object 实际上 null 之前说得更好),就会发生这种情况。 =50=]

所以一般来说:不要使用 == null 而是使用 bool 运算符来检查是否存在(尽管 Unity API 经常不幸地使用 == null 本身) .

更进一步:从不UnityEngine.Objetc使用?., ?? / ??= or !.运算符,因为它们也在底层System.Object上运行并忽略自定义==Equals 实施 UnityEngine.Object!