How do I check for nulls in an '==' operator overload without infinite recursion?

C#.NetOperator Overloading

C# Problem Overview


The following will cause infinite recursion on the == operator overload method

    Foo foo1 = null;
    Foo foo2 = new Foo();
    Assert.IsFalse(foo1 == foo2);

    public static bool operator ==(Foo foo1, Foo foo2) {
        if (foo1 == null) return foo2 == null;
        return foo1.Equals(foo2);
    }

How do I check for nulls?

C# Solutions


Solution 1 - C#

Use ReferenceEquals:

Foo foo1 = null;
Foo foo2 = new Foo();
Assert.IsFalse(foo1 == foo2);

public static bool operator ==(Foo foo1, Foo foo2) {
    if (object.ReferenceEquals(null, foo1))
        return object.ReferenceEquals(null, foo2);
    return foo1.Equals(foo2);
}

Solution 2 - C#

Cast to object in the overload method:

public static bool operator ==(Foo foo1, Foo foo2) {
    if ((object) foo1 == null) return (object) foo2 == null;
    return foo1.Equals(foo2);
}

Solution 3 - C#

Use ReferenceEquals. From the MSDN forums:

public static bool operator ==(Foo foo1, Foo foo2) {
    if (ReferenceEquals(foo1, null)) return ReferenceEquals(foo2, null);
    if (ReferenceEquals(foo2, null)) return false;
    return foo1.field1 == foo2.field2;
}

Solution 4 - C#

If you are using C# 7 or later you can use null constant pattern matching:

public static bool operator==(Foo foo1, Foo foo2)
{
    if (foo1 is null)
        return foo2 is null;
    return foo1.Equals(foo2);
}

This gives you slightly neater code than the one calling object.ReferenceEquals(foo1, null)

Solution 5 - C#

Try Object.ReferenceEquals(foo1, null)

Anyway, I wouldn't recommend overloading the == operator; it should be used for comparing references, and use Equals for "semantic" comparisons.

Solution 6 - C#

If I have overridden bool Equals(object obj) and I want the operator == and Foo.Equals(object obj) to return the same value, I usually implement the != operator like this:

public static bool operator ==(Foo foo1, Foo foo2) {
  return object.Equals(foo1, foo2);
}
public static bool operator !=(Foo foo1, Foo foo2) {
  return !object.Equals(foo1, foo2);
}

The operator == will then after doing all the null checks for me end up calling foo1.Equals(foo2) that I have overridden to do the actual check if the two are equal.

Solution 7 - C#

There is actually a simpler way of checking against null in this case:

if (foo is null)

That's it!

This feature was introduced in C# 7

Solution 8 - C#

My approach is to do

(object)item == null

upon which I'm relying on object's own equality operator which can't go wrong. Or a custom extension method (and an overload):

public static bool IsNull<T>(this T obj) where T : class
{
    return (object)obj == null;
}

public static bool IsNull<T>(this T? obj) where T : struct
{
    return !obj.HasValue;
}

or to handle more cases, may be:

public static bool IsNull<T>(this T obj) where T : class
{
    return (object)obj == null || obj == DBNull.Value;
}

The constraint prevents IsNull on value types. Now its as sweet as calling

object obj = new object();
Guid? guid = null; 
bool b = obj.IsNull(); // false
b = guid.IsNull(); // true
2.IsNull(); // error

which means I have one consistent/not-error-prone style of checking for nulls throughout. I also have found (object)item == null is very very very slightly faster than Object.ReferenceEquals(item, null), but only if it matters (I'm currently working on something where I've to micro-optimize everything!).

To see a complete guide on implementing equality checks, see https://stackoverflow.com/questions/104158/what-is-best-practice-for-comparing-two-instances-of-a-reference-type

Solution 9 - C#

The static Equals(Object, Object) method indicates whether two objects, objA and objB, are equal. It also enables you to test objects whose value is null for equality. It compares objA and objB for equality as follows:

  • It determines whether the two objects represent the same object reference. If they do, the method returns true. This test is equivalent to calling the ReferenceEquals method. In addition, if both objA and objB are null, the method returns true.
  • It determines whether either objA or objB is null. If so, it returns false. If the two objects do not represent the same object reference and neither is null, it calls objA.Equals(objB) and returns the result. This means that if objA overrides the Object.Equals(Object) method, this override is called.

.

public static bool operator ==(Foo objA, Foo objB) {
    return Object.Equals(objA, objB);
}

Solution 10 - C#

replying more to overriding operator how to compare to null that redirects here as a duplicate.

In the cases where this is being done to support Value Objects, I find the new notation to handy, and like to ensure there is only one place where the comparison is made. Also leveraging Object.Equals(A, B) simplifies the null checks.

This will overload ==, !=, Equals, and GetHashCode

    public static bool operator !=(ValueObject self, ValueObject other) => !Equals(self, other);
    public static bool operator ==(ValueObject self, ValueObject other) => Equals(self, other);
    public override bool Equals(object other) => Equals(other as ValueObject );
    public bool Equals(ValueObject other) {
        return !(other is null) && 
               // Value comparisons
               _value == other._value;
    }
    public override int GetHashCode() => _value.GetHashCode();

For more complicated objects add additional comparisons in Equals and a richer GetHashCode.

Solution 11 - C#

For a modern and condensed syntax:

public static bool operator ==(Foo x, Foo y)
{
    return x is null ? y is null : x.Equals(y);
}

public static bool operator !=(Foo x, Foo y)
{
    return x is null ? !(y is null) : !x.Equals(y);
}

Solution 12 - C#

> A common error in overloads of operator == is to use (a == b), (a ==null), or (b == null) to check for reference equality. This instead results in a call to the overloaded operator ==, causing an infinite loop. Use ReferenceEquals or cast the type to Object, to avoid the loop.

check out this

// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(a, b))// using ReferenceEquals
{
    return true;
}

// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))// using casting the type to Object
{
    return false;
}

reference Guidelines for Overloading Equals() and Operator ==

Solution 13 - C#

You can try to use an object property and catch the resulting NullReferenceException. If the property you try is inherited or overridden from Object, then this works for any class.

public static bool operator ==(Foo foo1, Foo foo2)
{
    //  check if the left parameter is null
    bool LeftNull = false;
    try { Type temp = a_left.GetType(); }
    catch { LeftNull = true; }

    //  check if the right parameter is null
    bool RightNull = false;
    try { Type temp = a_right.GetType(); }
    catch { RightNull = true; }
    
    //  null checking results
    if (LeftNull && RightNull) return true;
    else if (LeftNull || RightNull) return false;
    else return foo1.field1 == foo2.field2;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAndrew JonesView Question on Stackoverflow
Solution 1 - C#Abe HeidebrechtView Answer on Stackoverflow
Solution 2 - C#Andrew JonesView Answer on Stackoverflow
Solution 3 - C#Jon AdamsView Answer on Stackoverflow
Solution 4 - C#jacekbeView Answer on Stackoverflow
Solution 5 - C#Santiago PalladinoView Answer on Stackoverflow
Solution 6 - C#HallgrimView Answer on Stackoverflow
Solution 7 - C#Reto MesserliView Answer on Stackoverflow
Solution 8 - C#nawfalView Answer on Stackoverflow
Solution 9 - C#Zach PostenView Answer on Stackoverflow
Solution 10 - C#CCondronView Answer on Stackoverflow
Solution 11 - C#mr5View Answer on Stackoverflow
Solution 12 - C#Basheer AL-MOMANIView Answer on Stackoverflow
Solution 13 - C#The Digital GabegView Answer on Stackoverflow