You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中C#对象判定为null却仍可访问属性是何原因

Unity中“对象判为null却仍可访问属性”问题原因

这个现象是Unity引擎对UnityEngine.Object类型的特殊设计导致的,和标准C#的null判断逻辑存在本质区别:

  • 所有继承自UnityEngine.Object的类型(包括GameObject、Component、ScriptableObject、资源Asset等所有引擎原生对象),都重写了==运算符、隐式布尔转换逻辑、ToString()方法:它判定的“null”不是C#托管层面真的不存在有效对象引用,而是该C#包装对象对应的引擎C++侧原生实体已经被标记为销毁状态。
  • 触发这个“假null”状态的常见场景:手动对对象调用过Destroy()方法、切换场景时非DontDestroyOnLoad标记的对象被卸载、对象所属的AssetBundle被主动卸载释放、编辑器下退出播放模式时所有运行时临时对象被批量销毁。
  • 出现“能正常读取name属性、打印对象本身却返回null”的核心原因是销毁流程的时间差:原生对象被标记为销毁后,到C++侧内存被真正回收、C#侧包装对象被GC收集之间存在短暂的窗口期。这个阶段访问name、GetInstanceID()这类直接读原生侧内存的属性时,因为对应内存块还没被覆写释放,依然能读到正确的残留值;但打印对象本身、执行item == null判断时,走的是Unity重写的检查逻辑,只要检测到原生对象已被标记销毁,就会返回null结果。

要验证这个逻辑可以直接调用System.Object.ReferenceEquals(item, null),该方法不会触发Unity重写的相等运算符,返回值必然为false,足以证明C#托管层的对象引用真实存在,并非传统C#意义上的null。

问题对应的复现代码运行逻辑拆解:

// 内部调用原生接口读取残留内存中存储的name字段,可正常输出值
Debug.Log($"Item name is {item.name}");
// 调用Unity重写的Object.ToString(),检测到原生对象已销毁,直接输出null
Debug.Log($"Item is {item}");
// 走Unity重写的==运算符,判定为已销毁的假null,进入分支
if (item == null)
{
    Debug.Log("Yes, item is null!");
}

这个可读取残留值的窗口期非常短,后续再访问该对象的大部分引擎相关方法,就会抛出常见的MissingReferenceException: The object of type 'XXX' has been destroyed but you are still trying to access it异常。

内容的提问来源于stack exchange,提问作者Arnaud

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 12:42:16