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;
}
operator is known as Null-coalescing operator. It will return the value of its left-hand operand if it is not null. If it is null, then it will evaluate the right-hand operand and returns its result. Or if the left-hand operand evaluates to non-null, then it does not evaluate its right-hand operand.
Null-coalescing Operator Use this operator to fall back on a given value. In cases where a statement could return null, the null-coalescing operator can be used to ensure a reasonable value gets returned. This code returns the name of an item or the default name if the item is null.
The nullish coalescing operator ( ?? ) is a logical operator that returns its right-hand side operand when its left-hand side operand is null or undefined , and otherwise returns its left-hand side operand.
Object is destroyed, even if the object itself isn't actually null. Null propagation cannot be overridden in this way, and therefore behaves inconsistently with the == operator, because it checks for null in a different way.
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.
In general that's what the Object.bool
operator is used for. Just prefer to use it instead of a null
check:
public static T GetComponentOrAddIfMissing<T>(this GameObject self) where T : Component
{
T component = self.GetComponent<T>();
if(!component) component = self.AddComponent<T>();
return component;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With