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

Dra*_*wgo 6 c# resharper unity-game-engine

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Ten*_*tni 16

Unity 提供了一篇关于此主题的博客文章,但下面是一个简短的摘要。

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

为什么会出现这个建议IDamageable?因为它是一个接口。IDE(代码编辑器)不知道此接口实例的类型。它无法知道IDamageable继承自UnityEngine.Object,因此不会出现任何建议。ForceVelocityCalculator,然而,继承自MonoBehaviourScriptableObject,两者都继承自UnityEngine.Object。这很重要,因为 Unity 已经自定义了==操作符。这样,您习惯的默认相等比较就不会发生。

博文给出了这个决定的两个原因:

  1. 在编辑器中,Unity 有自己的 null 概念。的未初始化字段MonoBehaviour被赋予此 Unity 特定的空值。这与自定义==运算符相结合,让 Unity 在您开发时向您(开发人员)提供附加信息。相反,接收的NullReferenceException和标准的堆栈跟踪,而是收到一个增强的堆栈跟踪加上一些迹象,其中GameObject对存在的问题。博客文章提到了一个简洁的功能,它们突出显示GameObject了层次结构窗格中的问题。

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


Dra*_*18s 5

可能是因为 Unity 如何定义返回的内容GetComponent()

如果您真正了解 GetComponent 的作用,您会发现它实际上永远不会返回null,您总是会得到一个对象包装器,用于处理 C# 代码和 Unity 底层 C++ 代码之间的通信。

GetComponent()因此,当你从实际得到的内容中得到“null”时(Unity已经覆盖==运算符!)实际上并不是null,而是围绕null. 因此,ReSharper 不知道 null 传播在这里会有帮助(当然,假设它有效:因为对象实际上并不为null,只是假装为 null,所以语法糖可能无法正常工作!)。