Overriding Equals in C# (Part 3)
This post is part three in a series:
(View the completed example)
Overriding Equality Operators
Phew, we made it: we have now successfully implemented Equals
and GetHashCode
.
Let’s wrap things up by overriding the ==
operator.
Although Equals
now compares objects using value equality, the ==
operator still compares objects with reference equality, leading to the following inconsistent behavior:
PhoneNumber numberX = new PhoneNumber { AreaCode = "123", Exchange = "456", SubscriberNumber = "7890" };
PhoneNumber numberY = new PhoneNumber { AreaCode = "123", Exchange = "456", SubscriberNumber = "7890" };
numberX.Equals(numberY); // TRUE
numberX == numberY; // FALSE
We can fix this by overriding the ==
operator:
public static bool operator ==(PhoneNumber numberA, PhoneNumber numberB)
{
// Implementation
}
When we implement this, we need to be very careful not to use ==
as we could accidentally end up calling our override method again, resulting in an endless loop. Alright, let’s do this:
public static bool operator ==(PhoneNumber numberA, PhoneNumber numberB)
{
// Check if either of the numbers are null
if(Object.ReferenceEquals(null, numberA) || Object.ReferenceEquals(null, numberB))
{
return false;
}
// Check if the numbers are the same number
if(Object.ReferenceEquals(numberA, numberB))
{
return true;
}
return numberA.Equals(numberB);
}
Simplifying this further:
public static bool operator ==(PhoneNumber numberA, PhoneNumber numberB)
{
return !Object.ReferenceEquals(null, numberA)
&& !Object.ReferenceEquals(null, numberB)
&& (Object.ReferenceEquals(numberA, numberB) || numberA.Equals(numberB));
}
As Nathan Jackson points out (thanks Nathan!) in the comments below, this implementation leads to a situation where, if both instances are null, they will not be considered equal:
PhoneNumber phoneNumberA = null;
PhoneNumber phoneNumberB = null;
phoneNumberA == phoneNumberB; // FALSE, even though they are both the same value (null)
We can address this by simplifying our implementation even further:
public static bool operator ==(PhoneNumber numberA, PhoneNumber numberB)
{
return (Object.ReferenceEquals(numberA, numberB) || numberA.Equals(numberB));
}
However, as John points out (thanks John!) in the comments below, this implementation leads to a subtle bug when we try to call Equals
on numberA
:
PhoneNumber phoneNumberA = null;
PhoneNumber phoneNumberB = new PhoneNumber();
// Throws a null reference exception
if (phoneNumberA == phoneNumberB)
{
}
We can fix this by adjusting our solution slightly (thanks John!):
public static bool operator ==(PhoneNumber numberA, PhoneNumber numberB)
{
if (Object.ReferenceEquals(numberA, numberB))
{
return true;
}
// Ensure that "numberA" isn't null
if(Object.ReferenceEquals(null, numberA))
{
return false;
}
return (numberA.Equals(numberB));
}
Before we can finish this up, we also need to implement the opposite not equals operator (!=
). As long as we’re careful not to use !=
in our implementation, we can simply point back to our implementation of the ==
operator:
public static bool operator !=(PhoneNumber numberA, PhoneNumber numberB)
{
return !(numberA == numberB);
}