问题
I am trying to use the null-coalescing operator in a C# script in Unity, my project Scripting Runtime is set to .NET 4.x so it should work correctly.
The problem is that even though the LEFT operand evaluates to null, it does not correctly return the RIGHT operand.
Here is an example statement that does NOT work when the left Operand returns null:
m_meshFilter = ( GetMeshFilter() ?? AddMeshFilter() );
Here is the SAME exact statement, except it explicitly passes null to the null-coalescing operator (this works correctly when run)
m_meshFilter = ( GetMeshFilter() == null ? null : GetMeshFilter() ) ?? AddMeshFilter();
Here is a short script to easily test the problem directly in 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>();
}
}
You can run the test function by clicking the top right corner of the Component in the Inspector in Unity (2018.3.12f1)
When using the test script, the first use of the Null Coalescing operator will fail, but the second succeeds when I check for null explicitly, and then redundantly pass null (with the ternary operator)
Output in Unity Editor Console:
!Is this a bug? or am I doing something wrong?
_______________________________
EDIT:
In looking for a workaround I found a decent way to make return REAL null for the sake of the null-coalescing operator:
public static T GetComponentRealNull<T>( this GameObject self ) where T : Component
{
T component = self.GetComponent<T>();
if ( component == null )
return null;
return component;
}
Or for more specific cases of adding/getting components:
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;
}
回答1:
This is Unity screwing with you.
If you do this:
MeshFilter GetMeshFilter()
{
MeshFilter temp = myGo.GetComponent<MeshFilter>();
if ( temp == null ) {
Debug.Log( " > Get Mesh Filter RETURNING NULL" );
return null;
}
return temp;
}
It works.
Why?
Because Unity has overridden Equals
(and the ==
operator) on all Unity objects so that destroyed game objects and never existing objects are both "equal" to null (this was done in an attempt to make developer's lives easier). But a destroyed (or "missing") object is not literally null: its a wrapper object in the C# part of the engine pointing at a null object in the underlying C++ code. The null coalescing operator checks for literally null.
For example try this:
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
}
This is also why you get a MissingReferenceException when you try to access Unity objects that are null, rather than a NullReferenceException: those objects aren't literally null, but only effectively null.
来源:https://stackoverflow.com/questions/56875706/how-am-i-misusing-the-null-coalescing-operator-is-this-evaluating-null-correc