我如何滥用空合并运算符?这是正确评估 "null" 吗?

How am I misusing the null-coalescing operator? Is this evaluating "null" correctly?

我正在尝试在 Unity 的 C# 脚本中使用空合并运算符,我的项目脚本运行时设置为 .NET 4.x,因此它应该可以正常工作。

问题是即使 LEFT 操作数的计算结果为 null,它也不能正确地 return RIGHT 操作数。

这是一个示例语句,当左操作数 return 为空时 不起作用

m_meshFilter = ( GetMeshFilter() ?? AddMeshFilter() );

这是完全相同的语句,除了它显式地将 null 传递给 null 合并运算符(这在 运行 时正常工作)

m_meshFilter = ( GetMeshFilter() == null ? null : GetMeshFilter() ) ?? AddMeshFilter();

这是一个简短的脚本,可以直接在 Unity 中轻松测试问题。

using UnityEngine;

public class DoubleQuestionTest : MonoBehaviour
{
    public GameObject myGo;

    public MeshFilter m_meshFilter = null;

    [ContextMenu( "Test1 Null Coalescing Operator" )]
    void Test1()
    {
        m_meshFilter = ( GetMeshFilter() ?? AddMeshFilter() );

        if ( m_meshFilter == null )
        {
            Debug.Log( "m_meshFilter was null, trying Alternate" );
            m_meshFilter = ( GetMeshFilter() == null ? null : GetMeshFilter() ) ?? AddMeshFilter();
        }
    }

    MeshFilter GetMeshFilter()
    {
        MeshFilter temp = myGo.GetComponent<MeshFilter>();

        if ( temp == null )
            Debug.Log( "    > Get Mesh Filter RETURNING NULL" );

        return temp;
    }

    MeshFilter AddMeshFilter()
    {
        Debug.Log( "    > Add Mesh Filter Called" );
        return myGo.AddComponent<MeshFilter>();
    }
}

在Unity(2018.3.12f1)的Inspector中点击Component右上角可以运行测试功能

使用测试脚本时,第一次使用 Null Coalescing 运算符会失败,但第二次成功时我明确检查 null,然后冗余地传递 null(使用三元运算符)

Unity 编辑器控制台中的输出:

!

这是一个错误吗?还是我做错了什么?

_______________________________

编辑:

在寻找解决方法的过程中,我找到了一个不错的方法来使 return REAL null 为了 null 合并运算符:

    public static T GetComponentRealNull<T>( this GameObject self ) where T : Component
    {
        T component = self.GetComponent<T>();

        if ( component == null )
            return null;

        return component;
    }

或者对于 adding/getting 组件的更具体情况:

    public static T GetComponentOrAddIfMissing<T>(this GameObject self) where T : Component
    {
        T component = self.GetComponent<T>();
        if(component == null)
            component = self.AddComponent<T>();

        return component;
    }

这是 Unity 搞砸了。

如果你这样做:

MeshFilter GetMeshFilter()
{
    MeshFilter temp = myGo.GetComponent<MeshFilter>();

    if ( temp == null ) {
        Debug.Log( "    > Get Mesh Filter RETURNING NULL" );
        return null;
    }
    return temp;
}

有效。

为什么?

因为 Unity 已覆盖所有 Unity 对象的 Equals(和 == 运算符),因此 destroyed game objects 并且 never existing objects 都“等于”null(这样做是为了让开发人员的生活更轻松)。但是被销毁(或“丢失”)的对象并不是字面上的 null:它是引擎 C# 部分中的一个包装对象,指向底层 C++ 代码中的 null 对象。空合并运算符检查 字面上的空值。

例如试试这个:

Start() {
    GameObject gg = new GameObject(); //create a GO
    DestroyImmediate(gg); //destroy it immediately
    Debug.Log(gg == null); //prints true: it is definitely null!
    GameObject go = gg ?? this.gameObject; //get a non-null object
    Debug.Log(go); //prints null
}

这也是为什么当您尝试访问为 null 的 Unity 对象时得到 MissingReferenceException 而不是 NullReferenceException 的原因:这些对象并非字面上 null,而只是 有效无效。


一般来说,这就是 Object.bool operator 的用途。只是更喜欢使用它而不是 null 检查:

public static T GetComponentOrAddIfMissing<T>(this GameObject self) where T : Component
{
    T component = self.GetComponent<T>();

    if(!component) component = self.AddComponent<T>();

    return component;
}