?? operator not working as expected

According to ?? and ??= operators - null-coalescing operators | Microsoft Learn

The null conditional operator returns the left value if the left value is != null and the right value otherwise. However, the following two code blocks do not return the same result. Where am I thinking wrong/what don’t I see?

//Works and does not leave x == null
var x = o.GetComponent<Renderer>();
if(x==null) x = o.GetComponentInChildren<Renderer>();

//Ternary operator also works and does not leave x == null
var x = o.GetComponent<Renderer>() != null ? o.GetComponent<Renderer>() : GetComponentInChildren<Renderer>();

//null coalescing operator does not work and leaves x as == null
var x = o.GetComponent<Renderer>() ?? o.GetComponentInChildren<Renderer>();

Got this from another thread:

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.

See best answer. It’s correct, but there are more things to say.

1: The new version of Unity works with C# v6 and Framework 4.6, so this may no longer be true. I hope they fixed it.

2: You can always override the overrides by casting to object (but this can make the code clunky).

// cast to object to avoid Unity weirdness
var x = (o.GetComponent<Renderer>() as object) 
        ?? (o.GetComponentInChildren<Renderer>() as object) as Renderer;

3: For many cases it’s better to use the “null conditional” operator .? but this unfortunately isn’t one of them.

And @dpoly for the same reason, your point 3) about the null conditional operator .?, this doesn’t work either. E.g.

CurrentGame = CurrentGameController?.GetComponentInChildren();

doesn’t work.