C#: ?? operator

The original code:

MeshFilter meshFilter;
if (gameObject.GetComponent<MeshFilter>() != null)
{
	meshFilter = gameObject.GetComponent<MeshFilter>();
}
else
{
	meshFilter = gameObject.AddComponent<MeshFilter>();
}

meshFilter.mesh = someMesh;

I’m trying to rewrite it into

MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>() ?? gameObject.AddComponent<MeshFilter>();

meshFilter.mesh = someMesh;

that is intended to do absolutely the same, but meshFilter still remains null, and I get the error message:

Why meshFilter is null?

Did you try making meshFilter public so you can attach a filter?

-Tony

MeshFilter component is attached to gameObject. meshFilter variable just stores its reference.

MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>();
if( !meshFilter ) meshFilter = gameObject.AddComponent<MeshFilter>();
meshFilter.mesh = someMesh;

I don’t think you can do that in C#, BigMisterB. You probably need to explicitly turn the check into a boolean operation:
if (meshFilter == null)

It can be done actually ― http://unity3d.com/support/documentation/ScriptReference/Object-operator_bool.html?from=Component. But anyway the question is about the ?? operator behavior.

Oooh, I didn’t know they added the operator overload for the UnityEngine.Object class.

(as an aside, I find it terribly confusing that they chose that name… >< )

the ?? is a standard .net operator which basically does the same as “if reference == null, then return XXX”, just much shorter: reference ?? XXX

problem is that it has some limitations and “requirements”. The MSDN has some information on the C# operator documentation for it.

I personally never use it, I hate unreadable code and “idiotically short if you have auto complete and intellisense” is unreadable out of my view.

Already ?: is only used if the expression to evaluate is simple and the then and else blocks are very short and of simple nature (like directly a number, a string or an int.parse(…))

You could always use the single ? instead:

MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>() ? 
			gameObject.GetComponent<MeshFilter>() : gameObject.AddComponent<MeshFilter>();

Not really sure why you’d want to though.

1 Like

Guys, I’m asking not how to write code without ??, but why this operator doesn’t work as expected. I checked it again:

Before:
― meshFilter is undeclared,
― gameObject.GetComponent() returns null

var meshFilter = gameObject.GetComponent<MeshFilter>() ?? gameObject.AddComponent<MeshFilter>();

After:
― meshFilter is null,
― gameObject.GetComponent() returns null


This variant doesn’t work either:

var meshFilter = gameObject.GetComponent<MeshFilter>();
// at this moment meshFilter is null

meshFilter = meshFilter ?? gameObject.AddComponent<MeshFilter>();
// meshFilter is null

I’m confused.


And this one also doesn’t work:

var meshFilter = gameObject.GetComponent<MeshFilter>();
// at this moment meshFilter is null

var newMeshFilter = gameObject.AddComponent<MeshFilter>();
// newMeshFilter is not null

meshFilter = meshFilter ?? newMeshFilter;
// meshFilter is null

This line does work

var meshFilter = null ?? gameObject.AddComponent<MeshFilter>();

but I suspect it is compiled into

var meshFilter = gameObject.AddComponent<MeshFilter>();

However this also works:

string firstOperand = Mathf.Pow(1, 1) < float.MaxValue ? null : "first"; // should be null
string secondOperand = "second";

string result = firstOperand ?? secondOperand;
print(string.Format("{0} ?? {1} == {2}", firstOperand ?? "NULL", secondOperand, result));

It prints “NULL ?? second == second”

I know this is an old thread, but I’ve been bit by this numerous times in the past as well and the OP’s question remains unanswered, so I’m replying the sake of any others that come across this in the future.

The reason why ?? sometimes works and sometimes does not with GameObjects & Components is because their base class, UnityEngine.Object, defines its own operator ==(), operator !=(), & operator bool():

Unity’s engine runtime uses these to report the managed (C#/.NET) object instance as null when its engine-native (C++) object instance hasn’t been created yet or has already been deleted. (Most .NET classes you see in UnityEngine/UnityEditor are only part of the story; a portion of their functionality exists only in native binaries for each CPU/platform and shipped pre-compiled inside of the Unity app.)

So using ?? with UnityEngine.Object-derived instances is unreliable and unadvised because a given instance may either be truly-null or just faking-null, and because C# does not allow overloading of the ?? operator, and because .NET’s implementation of ?? checks whether the instance is actually null not what the operator==() tells it.

TL;DR version: Don’t use ?? with UnityEngine.Object derivatives (GameObjects, Components, MonoBehaviours, ScriptableObjects, etc.). It’ll work inconsistently (compared to normal ==null checks) and will make your code hair-pullingly buggy.

6 Likes

I wouldn’t say it’s inconsistent.

I’d say that it’s like saying:

MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>()?? gameObject.AddComponent<MeshFilter>();

Is not like saying:

MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>();
if(meshFilter == null) gameObject.AddComponent<MeshFilter>();

And more like saying:

MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>();
if(object.ReferenceEquals(meshFilter, null)) gameObject.AddComponent<MeshFilter>();

It’s consistent in doing this, it’s just not obvious.

Note, it technically calls object.ReferenceEquals… it’s actually the il code “brtrue.s”. There’s no real way to actually call this in C#, it’s just an optimized command in IL. The result of it is equivalent to if you said ‘object.ReferenceEquals(a, null)’.

1 Like

@lordofduct .NET’s behavior is consistent, yes, even though it’s obtuse. For many GetComponent/AddComponent use-cases, it’ll behave the same on every call.

What makes it inconsistent is the fact that the managed UnityEngine.Object instance’s lifecycle does not match its native instances’s lifecycle. Inconsistencies therefore manifest early in initialization and around the time of destruction. What seems consistent can do strange things intermittently or only on specific platforms when loading/unloading scenes, when dealing with Asset Bundles, or other times (my apologies for the lack of specificity; I’ve been careful not to encounter situations like this for years).

I advocate against using it because:

  • It works counter-intuitively to C#'s design— ?? is supposed to check for null, but it doesn’t work with UnityEngine.Objects.
  • As a Unity developer, you have little to no control over what the runtime decides to do. It unserializes/initializes objects in your scenes & prefabs when it wants to, and frees them at its leisure. The guarantees afforded in Unity’s docs are low, and the priority to keep FPS high on each deployment platform is high. Ultimately, the behavior of these objects near-init and near-destruction is not in your hands.
2 Likes

but it DOES check for null

it legitimately checks for null, rather than access the null test that unity overrides

this makes it consistent, it’s just as consistent as if you were to say “object.ReferenceEquals”.

What’s obtuse is the fact Unity overloaded the method making obj == null not be the same as object.ReferenceEquals(obj, null). The only inconsistency is in regards to Unity, not .Net.

And I wouldn’t say avoid ??, as long as you understand WHAT is actually occurring with the operation… that it’s testing ACTUAL null (as opposed to unity’s bastardized destroyed test). With that knowledge (which is unity’s fault for obfuscating), you can safely use it all you want.

@lordofduct Half of what you’ve written here is repeating things I’ve already said, and half is skirting over everything I’ve said about Unity’s native/C++ runtime. Everything I could say at this point to counter your argument I’ve already said. Please go up and re-read both of my replies; it seems you’ve combined your understanding with a quick skim of my posts rather than digesting the full extent of my advice.

The difference between what you’re saying and what I’m saying is that I don’t think it’s a reason to AVOID using the operator.

The fact is, it’s a shortcut that doesn’t exhibit the usual and intended behaviors of that operator (which is a type of inconsistency), and as slippdouglas rightly points out, that makes a compelling argument for just avoiding it. It’s not like it provides some type of functionality that is otherwise impossible to achieve.

Plus all this nitpicking detracts from the main thrust of the original slippdouglas post which is to explain what is happening and why. If somebody comes here needing that explanation, then it’s very likely they don’t understand it well enough to risk using it anyway, so why try to talk anyone into it?

I disagree, it exhibits exactly its intended behaviour!

It exhibits the behaviour of testing if a managed object reference exists or not, that IS the intended behaviour. It consistently evaluates that very thing. The fact that unity allows “== null” to be inconsistent with the intended behaviour of testing if a managed object reference exists, and instead makes it mean “true if the managed object does not exist OR if the unmanaged unity object has been destroyed”.

?? is NOT included in that.

Which means ?? acts EXACTLY like the mono/.net intent of the operator.

In that it means:

MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>();
if(object.ReferenceEquals(meshFilter, null)) gameObject.AddComponent<MeshFilter>();

This is consistent behaviour, it always means that above.

And I do not believe people should be told to NOT use it. But rather that they should understand why (and props to slipdouglas for explaining part of that in his post).

I’m not trying to talk anyone into using it either, I’m trying to state that it’s no reason to be afraid of it. There’s far too many "I won’t touch that because I don’t understand it"s, which I find ignorant and close minded to learning new things (note, I’m not saying slipdouglas is ignorant, he clearly understands why and has made an informed choice of aversion, but I think the post he makes promotes aversion by others out of fear).

Understand the thing.

If this is nitpicking… why? Slipdouglas posted their OPINION. I posted my OPINION counter to it. Slipdouglas is FREE to continue avoiding the operator all he wants.

3 Likes