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

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

提问人:Иван Поздняков 提问时间:3/28/2022 最后编辑:Иван Поздняков 更新时间:3/28/2022 访问量:511

问:

程序属于这个分支,仍然没有暗恋,这是非常出乎意料的。对象被销毁并为 null,但所有数据仍然相同。据我了解,它应该因空引用异常而粉碎。我是否遗漏了一些关于 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);
}

beginning 是 Node 类型的对象:

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);
}

调试器:enter image description here

C# .NET unity-game-engine null nullreferenceexception

评论

0赞 MakePeaceGreatAgain 3/28/2022
你到底在哪里期望例外?你是如何检查一个“对象被销毁和空”的?请显示“销毁”对象的代码。
1赞 Max Play 3/28/2022
还要注意的是,是的,Unity 会用 null 和 MonoBehaviours 做一些奇怪的事情。您是否尝试过使用调试器?另外,您是否将其显式设置为 null,或者您只是假设它应该为 null,因为您调用了“Destroy()”?
0赞 Andrew Corrigan 3/28/2022
@MakePeaceGreatAgain该语句表明对象的值确实是 null(如果它进入内部块);但是,鉴于该类中的所有值都有一个默认值(并且似乎没有构造函数来实例化该类),我怀疑这就是他的困惑的来源if
0赞 Иван Поздняков 3/28/2022
@AndrewCorrigan 不是那个。如果 object 确实是 null,则也找不到它所引用的类类型。而且,打印值不是默认值。
0赞 Иван Поздняков 3/28/2022
@MaxPlay我假设的。但它去了这个早午餐,它应该是空的。我在帖子中添加了调试结果。似乎 null 比较被覆盖,它实际上检查对象是否被销毁。但是为什么只删除了与 Unity 相关的内容,而删除了 Node 本身呢?以后会用垃圾回收器删除吗?很奇怪...

答:

2赞 derHugo 3/28/2022 #1

长话短说:一般来说,对于继承自的事物,在检查是否存在时,请使用布尔运算符UnityEngine.Object

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

请注意,Unity 有一个 Custom == 实现另请参阅此处的进一步说明),用于继承自 的任何内容。UnityEngine.Object

这个习俗基本上是基于==

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.Objectobjectnullc#==c++IsNativeObjectAlive

请注意,Unity 没有显示,而是一个自定义,表明这实际上不是真的,而是返回 的 Unity 运算符,即使仍然存在。NullReferenceExceptionMissingReferenceExceptionSystem.Objectnull==truenullSystem.Object

一旦你销毁了一个对象,然后尝试在同一帧中访问它(或者在GC收集该实例并使其实际存在之前,最好说一下)就会发生这种情况。System.Objectnull

所以一般来说:不要使用运算符,而是使用运算符来检查是否存在(即使 Unity API 经常使用自己)。== nullbool== null

更进一步:也永远不要使用 ?., ?? / ? = 或 !. 运算符,因为它们也对底层进行操作,并且忽略了 UnityEngine.ObjetcSystem.Object==EqualsUnityEngine.Object