Search code examples
c#.netnullthisreferenceequals

Why would I ever want to do object.ReferenceEquals(null, this) in Equals override?


Consider the following code that I was reviewing:

public override bool Equals(object other)
{
    return !object.ReferenceEquals(null, this)
           && (object.ReferenceEquals(this, other)
           || ((other is MyType) && this.InternalEquals((MyType)other)));
}

The first line in this code triggered my curiosity. Whenever this is null, the method should return false. Now I am pretty sure the programmer meant to write !object.ReferenceEquals(other, null), to shortcut situations with null, but he's insistent that this can be null. I'm insistent that it cannot (unless someone uses direct memory manipulation). Should we leave it in?


Solution

  • While I certainly wouldn't normally check this for nullity, it's possible, without any actual memory nastiness - just a bit of reflection:

    using System;
    
    public class Test
    {
        public void CheckThisForNullity()
        {
            Console.WriteLine("Is this null? {0}", this == null);
        }
    
        static void Main(string[] args)
        {
            var method = typeof(Test).GetMethod("CheckThisForNullity");
            var openDelegate = (Action<Test>) Delegate.CreateDelegate(
                   typeof(Action<Test>), method);
            openDelegate(null);
        }
    }
    

    Alternatively, generate IL which uses call instead of callvirt to call an instance method on a null target. Entirely legit, just not something the C# compiler would normally do.

    This has nothing to do with finalization, which is hairy in its own right but in different ways. It's possible for a finalizer to run while an instance method is executing if the CLR can prove that you're not going to use any fields in the instance (which I would strongly expect to include the this reference).

    As for the code presented - nope, that looks like it's just a mistake. I would rewrite it as:

    public override bool Equals(object other)
    {
        return Equals(other as MyType);
    }
    
    public bool Equals(MyType other)
    {
        if (ReferenceEquals(other, null))
        {
            return false;
        }
        // Now perform the equality check
    }
    

    ... assuming that MyType is a class, not a struct. Note how I'm using another public method with the right parameter type - I'd implement IEquatable<MyType> at the same time.