Search code examples
c#unity-game-engineobjectnullnull-check

Unity "null" == Check Failing?


https://i.sstatic.net/jyYpi.gif So I've been doing Unity C# programming for about 6 years now, and I only JUST found out about C# null v.s. Unity "null" (that's what you get when Unity is how you learn C# I guess). Needless to say, quite a spanner in the works, as I've been using the ?. operator tons in projects over the years which simply won't work with Unity "null" due to the fact that Unity is written in C++ and C# is just the layer in which projects are written, Unity "null" AFAIK really meaning the UnityEngine.Object is destroyed but the reference is still there for debugging purposes only becoming C# null once GC'd whenever that is.

I've done some looking into the differences between Unity "null" and C# null, and I'm pretty confused on why I'm still having null-checking problems. You can see in the above GIF that inst is clearly Unity "null" and not C# null. I do a comparison that reads inst==null which from what I have been told should be true in this context since Unity overrides the == operator for all UnityEngine.Object instances and accounts for this as opposed to not considering things like ?.. However, as you can also see in the GIF, the bool that stores the result of this == comparison (for debugging purposes) is false, which is what confuses me, I'm explicitly using the == operator I'm told UnityEngine.Object overrides for exactly this situation yet I'm getting the same results as if I weren't to be comparing explicitly.

Do note a few things about the provided GIF:

  1. Some of the function in the GIF is needlessly verbose to help me debug this issue
  2. TRefType is contracted by where to be a class, so it can be an object or a UnityEngine.Object and I need a way to null-check for either.

What's going on?! This whole C# null v.s. Unity "null" thing alone has messed up my flow, and now my understanding of the issue might not be completely there either! For more info on the whole topic, assuming you're as lost as I was when I found out about Unity "null", check this out: https://blog.unity.com/technology/custom-operator-should-we-keep-it

Here are some other StackOverflow threads I checked out before making this post that only further convince me that my current understanding is correct and yet I'm still not getting the results I expect: Why does C# null-conditional operator not work with Unity serializable variables? Unity : this == null returns true. How can this happen?

Here is the function I'm working with in isolation of the rest of my project as a code block for your convenience in replying. You can just ignore the other case outcomes, they are acting as-intended right now:

        private bool IsNull(TRefType inst)
        {
            Type instType = null;

            if(inst!=null)
                instType = inst.GetType();

            switch (instType)
            {
                case Type t when t == typeof(string):
                    return string.IsNullOrWhiteSpace(inst.ToString());
                case Type t when t == typeof(IEnumerable):
                    if (t != typeof(IDictionary))
                        return CollectionHelpers.AnyAllScanners.AnyElementsNotNull(inst as IEnumerable);
                    else
                        return CollectionHelpers.AnyAllScanners.AnyElementsNotNull(inst as IDictionary);
                default:
                    bool iN = inst==null;
                    bool iTN = instType == null;
                    return iN || iTN;
            }
        }

Any help greatly appreciated!


Solution

  • It seems that IsNull is part of some generic class and for all reference types used as generic type parameter the generic method implementation will be shared (i.e. compiler will produce one method for all reference types passed as generic type parameter to prevent the code bloat) so the operator resolution happens at compile time, hence in this case this is "C#'s null".

    You add check for UnityEngine.Object:

    switch (instType)
    {
        // ...
        case _ when typeof(UnityEngine.Object).IsAssignableFrom(instType):
            return (inst as UnityEngine.Object) == null; 
        // ...
    }