Unity-Resharper-可能意外绕过Unity引擎对象的底层生命周期检查

(Ryuu: 附上原文地址 : Possible unintended bypass of lifetime check of underlying Unity engine object · JetBrains/resharper-unity Wiki)

这是 Unity 特定的检查。此检查仅在 Unity 项目中运行。

若从 UnityEngine.Object 派生的类型使用空合并 (??) 或空传播或条件 (?.) 运算符,则会显示此警告。 这些运算符不会使用 UnityEngine.Object 上声明的自定义相等运算符,将绕过 Unity 原生(native)对象的存活检测。 为了阐明意图,最好使用显式 null 或 bool 比较,或调用 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 或布尔比较的显式比较。若不打算进行生命周期检查,请调用 System.Object.ReferenceEquals() 以明确表意。注意,对 object.ReferenceEquals() 的调用被编译器优化为简单的空检查,比调用自定义相等运算符更快。

空合并运算符

以下示例的表意不明确:是检查 gameObject是否正确引用,还是检查原生 Unity 引擎对象是否已销毁?

1
var go = gameObject ?? CreateNewGameObject();

若目的是检查底层引擎对象的生命周期,则此代码不正确,因为生命周期检查被绕过。使用显式 null 或 boolean 比较修复代码:

1
2
3
var go = gameObject != null ? gameObject : CreateNewGameObject();
// 也可使用隐式的 bool 转换运算符进行同样的检测
go = gameObject ? gameObject : CreateNewGameObject();

若目的是确保 gameObject 变量已被初始化并分配了有效的 C# 引用,推荐使用显式调用 object.ReferenceEquals():

1
return !object.ReferenceEquals(gameObject, null) ? gameObject : CreateNewGameObject();

虽然这种更改稍显冗长,但表意十分明确。

空条件运算符

以下示例的表意同样不明确:

1
monoBehaviour?.Invoke("Attack", 1.0f);

同样的,如果目的是简单地检查 monoBehaviour 变量是否已正确初始化与引用,推荐使用显式调用 object.ReferenceEquals():

1
2
if (!object.ReferenceEquals(monoBehaviour, null))
monoBehaviour.Invoke("Attack", 1.0f);

但是,如果目的是检查底层引擎对象的生命周期,推荐使用显式的 null 或 boolean 比较:

1
2
3
4
5
if (monoBehaviour != null)
monoBehaviour.Invoke("Attack", 1.0f);
// 也可使用隐式的 bool 转换运算符
if (otherBehaviour)
otherBehaviour.Invoke("Attack", 1.0f);

参阅

有关此主题的更多详细信息,请参阅 Unity 博客文章 “Custom == operator, should we keep it?”.

已翻译:Unity-Resharper-可能意外绕过Unity引擎对象的底层生命周期检查