代码检查:可能无意中绕过了底层 Unity 引擎对象的生命周期检查(在派生自 'UnityEngine.Object' 的类型上进行 null 传播会绕过底层 Unity 引擎对象的生命周期检查)
如果派生自 UnityEngine.Object 的类型使用 Null 合并 (?? )或 空传播或条件 (?. )运算符,则会显示此警告。 这些运算符不会使用声明在 UnityEngine.Object 上的自定义等于运算符,因此会绕过对底层原生 Unity 引擎对象是否被销毁的检查。 建议使用显式的 null 或布尔比较,或调用 System.Object.ReferenceEquals() 来明确意图。
详情
派生自 UnityEngine.Object 的类型是托管的 .NET 对象,使用 C# 脚本来表示并操作原生 Unity 引擎对象。 这两种对象具有不同的生命周期。 .NET 托管对象在没有更多引用时会被垃圾回收,而原生 Unity 引擎对象会在加载新场景或通过显式调用 UnityEngine.Object.Destroy() 时被销毁。 这意味着可能存在托管 .NET 对象引用了已被销毁的原生对象的情况。
UnityEngine.Object 类定义了自定义的等于运算符,在与 null 进行比较时会检查底层原生 Unity 引擎对象是否已被销毁。 换句话说, myMonoBehaviour == null 会检查 myMonoBehaviour 变量是否已赋值,并且会判断原生引擎对象是否已被销毁。 同样的检查也可以通过布尔比较实现,例如 if (myMonoBehaviour == true)、 if (!myMonoBehaviour) 或仅 if (myMonoBehaviour)。
如果使用了 Null 合并或条件运算符,则意图并不明确,并且可能绕过了生命周期检查。 如果需要检查生命周期,建议显式与 null 比较,或进行布尔比较。 如果不需要生命周期检查,请通过调用 System.Object.ReferenceEquals() 明确意图。 请注意,调用 object.ReferenceEquals() 会被编译器优化为简单的 null 检查,比调用自定义等于运算符更加高效。
空值合并运算符
在以下示例中,意图并不明确。 这是在检查 gameObject 变量是否已正确赋值,还是在检查原生 Unity 引擎对象是否已被销毁?
如果目的是检查底层引擎对象的生命周期,则此代码是错误的,因为生命周期检查已被绕过。 请通过显式 null 或布尔比较修正代码:
如果目的是确保 gameObject 变量已初始化并被赋予有效的 C# 引用,则建议显式调用 object.ReferenceEquals():
虽然这些更改略显冗长,但现在意图已经明确。
空条件运算符
此示例在意图方面也不明确:
同样,如果只是检查 monoBehaviour 变量已正确初始化并赋值,则建议显式调用 object.ReferenceEquals():
但如果目的是检查底层引擎对象的生命周期,则建议显式 null 或布尔比较:
关于此主题的更多详细信息,请参阅 Unity 博客文章 "自定义 == 运算符,我们该保留它吗?"。