How to properly check for null in generic arguments

I just discovered that I still had not properly learned everything I needed about Unity’s null references.

What I found is that when writing collection code, generic fields might still report == null as false, even when object has no value, and its ToString() is “null”.

So my question is… how do you properly check a generic argument for equality between null and “null” (the other null).

1 Like

To my understanding:

If the type is explicitly Unity type (Unity.Object and derived → Component Monobehavior etc) than it will return null when an object was destroyed even when reference is not null. ex: void Foo(Component cmp)

But when your object type is hidden for example void Foo(object obj) than it will not return null when it was destroyed.

To check for real null on Unity type you can use System.object.ReferenceEquals or cast Unity type to System.object and than compare.

My colleague suggested this

if (obj is UnityEngine.Object uobj)
    return uobj == null;
return obj == null;

If I did reference equals or System.object ==, then the “null” objects would not be treated as equal to real null, which was my problem.

1 Like

I think this can work

using System.Collections.Generic;

public class EqualityComparerForUnity<T> {

    public static IEqualityComparer<T> Default { get; } = CreateComparer();

    private static IEqualityComparer<T> CreateComparer() {

        if (typeof(UnityEngine.Object).IsAssignableFrom(typeof(T)))
            return (IEqualityComparer<T>) new UnityObjectEqualityComparer();

        return EqualityComparer<T>.Default;
    }

    private class UnityObjectEqualityComparer : EqualityComparer<UnityEngine.Object> {
        public override bool Equals      (UnityEngine.Object left, UnityEngine.Object right) => left?.Equals(right) ?? right?.Equals(left) ?? true;
        public override int  GetHashCode (UnityEngine.Object obj)                            => obj?.GetHashCode() ?? 0;
    }
}

Well, this is the general issue when you have a System.Object reference that holds a UnityEngine.Object instance. That reference could be real null, or could reference a dead / fake null UnityEngine.Object instance which should be treated as if it’s null. This is usually handled by the overloaded == operator, however operators are not polymorphic. The simplest ad hoc solution is to use

if (obj == null || obj.Equals(null))

instead of

if (obj == null)

Of course when you need the opposite condition, apply the usual boolean logic. So you use

if (obj != null && !obj.Equals(null))

instead of

if (obj != null)

This will work for all objects since UnityEngine.Object does override the Equals method which is polymorphic. Though it can only be used when you actually have an instance. That’s why you need the first condition as well so that Equals is only called when you actually have an instance.

3 Likes