代码检查:可能无意中绕过了底层 Unity 引擎对象的生命周期检查(对派生自 UnityEngine.Object 的类型进行模式匹配的 null 检查会绕过底层 Unity 引擎对象的生命周期检查)
如果某个派生自 UnityEngine.Object 的类型使用了 null 合并 (?? )或 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 检查,速度比调用自定义相等运算符更快。
Null 合并运算符
在以下示例中,意图并不明确。 这是在检查 gameObject 变量是否已正确赋值,还是在检查原生 Unity 引擎对象是否已被销毁?
如果目的是检查底层引擎对象的生命周期,则此代码是不正确的,因为生命周期检查已被绕过。 请用显式 null 或布尔比较修正代码:
如果意图是确保 gameObject 变量已初始化并分配了有效的 C# 引用,建议显式调用 object.ReferenceEquals():
虽然这些修改更为啰嗦,但意图现在很明确。
Null 条件运算符
此示例对意图同样不明确:
同样,如果只是简单检查 monoBehaviour 变量已经正确初始化并赋值,建议显式调用 object.ReferenceEquals():
如果目的是检查底层引擎对象的生命周期,建议显式 null 或布尔比较:
有关此主题的更多详情可参见 Unity 博客文章 "Custom == 运算符,是否应该保留?"。