为什么 ReSharper 不建议对这两个块使用空传播?

Why does ReSharper not suggest using null propagation for both of these blocks?

ReSharper 建议对“易损坏”if 块使用空传播,但对于“forceVelocityCalculator”没有任何建议。

void Damage(Collider hitCollider)
{
    var damageable = hitCollider.GetComponent<IDamageable>();

    if (damageable != null)
    {
        damageable.TakeDamage(_damage);
    }
}

void Push(Collider hitCollider)
{
    var forceVelocityCalculator = hitCollider.GetComponent<ForceVelocityCalculator>();

    if (forceVelocityCalculator != null)
    {
        forceVelocityCalculator.Push(_pushForce, GameAPI.playerTransform.transform.forward);
    }
}

我错过了什么吗?我会为两个块使用空传播。

可能是因为 Unity 如何定义从 GetComponent()

返回的内容

如果你 真的 看看 GetComponent 做了什么,它实际上 never returns null,你总是得到一个对象包装器来处理您的 C# 代码和 Unity 的底层 C++ 代码之间的通信。

因此,当您从 GetComponent() 中获得“null”时,您实际获得的 (Unity has overridden the == operator!) 实际上并不是 null,而是一个对象包装器 around一个null。所以 ReSharper 不知道 null 传播在这里会有帮助(当然,假设它有效:因为对象不是 actually null,只是假装为 null,语法糖可能无法正常工作!)。

Unity 提供了关于此主题的 blog post,但随后是一个简短的摘要。

您的组件继承的 Unity 对象上的空值传播是不正确的。 Resharper 不建议这样做,Visual Studio 2019 对此发出警告。

为什么会出现 IDamageable 的建议?因为它是一个接口。 IDE(代码编辑器)不知道此接口实例的类型。它不知道 IDamageable 继承自 UnityEngine.Object,因此不会出现提示。然而,ForceVelocityCalculator 继承自 MonoBehaviourScriptableObject,两者均继承自 UnityEngine.Object。 这很重要,因为 Unity 自定义了 == 运算符。这样一来,你习惯的默认相等比较就不是这样了。

博客 post 给出了做出此决定的两个原因:

  1. 在编辑器中,Unity 有自己的 null 概念。 MonoBehaviour 的未初始化字段被赋予这个特定于 Unity 的空值。这与自定义 == 运算符相结合,让 Unity 在您开发时为您(开发人员)提供额外的信息。您不会收到 NullReferenceException 和标准堆栈跟踪,而是收到增强的堆栈跟踪以及一些指示 GameObject 存在问题的指示。博客 posts 提到了一个巧妙的功能,它们在层次结构窗格中突出显示有问题的 GameObject

  2. 由于 Unity 是 C/C++ 引擎并且您使用 C# 编写脚本,因此您可以将 C# 对象 "wrapping" 视为 C++ 对象。有关该游戏对象的所有信息(附加组件、HideFlags 等)都在 C++ 对象中。此外,这些 C++ 对象的生命周期是明确管理的。这就是为什么您使用 Object.Destroy() 而不是将其设置为 null 的原因。自定义 == 运算符解决了 C++ 对象已被销毁但 "wrapping" C# 对象仍然存在的情况。在这种情况下,CSharpObject == null returns 为真,即使您的 C# 对象在技术上不为空。