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(…))
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>();
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.
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)’.
@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.
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 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.
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.