I have implemented the IEquatable interface in a class with the following code.
public bool Equals(ClauseBE other)
{
if (this._id == other._id)
{
return true;
}
return false;
}
public override bool Equals(Object obj)
{
if (obj == null)
{
return base.Equals(obj);
}
if (!(obj is ClauseBE))
{
throw new InvalidCastException("The 'obj' argument is not a ClauseBE object.");
}
return Equals(obj as ClauseBE);
}
public override int GetHashCode()
{
return this._id.GetHashCode();
}
public static bool operator ==(ClauseBE a, ClauseBE b)
{
// cast to object so we call the overloaded Equals function which appropriately checks when b is null.
return a.Equals(b as object);
}
public static bool operator !=(ClauseBE a, ClauseBE b)
{
// cast to object so we call the overloaded Equals function which appropriately checks when b is null.
return !a.Equals(b as object);
}
This code work very well for most all cases. However, the following check throws an exception in the equality operator overload method because a is null and therefore does not have a Equals method.
if(this.Clause != null)
{
}
What is the standard way to solve this issue?
EDIT
I have gone to this, but it seems pretty cumbersome. I was hoping there was a more elegant way to accomplish this.
public static bool operator ==(ClauseBE a, ClauseBE b)
{
if (a as object == null && b as object == null)
{
return true;
}
if ((a as object == null && b as object != null)
|| (b as object == null && a as object != null))
{
return false;
}
// cast to object so we call the overloaded Equals function which appropriately checks when b is null.
return a.Equals(b as object);
}
public static bool operator !=(ClauseBE a, ClauseBE b)
{
if (a as object == null && b as object == null)
{
return false;
}
if((a as object == null && b as object != null)
|| (b as object == null && a as object != null))
{
return true;
}
// cast to object so we call the overloaded Equals function which appropriately checks when b is null.
return !a.Equals(b as object);
}
Solution
Thanks all. I got a lot of good tips from everyone, I really appreciate it. This is what I finally settled on, it's a lot more elegant than what I had started with. All code is the same except operator overloads.
public static bool operator ==(ClauseBE a, ClauseBE b)
{
if (ReferenceEquals(a, null) && ReferenceEquals(b, null))
{
return true;
}
if (ReferenceEquals(a, null) || ReferenceEquals(b, null))
{
return false;
}
return a.Equals(b);
}
public static bool operator !=(ClauseBE a, ClauseBE b)
{
return !(a == b);
}
I prefer to perform all the comparison logic in the Equals(T) method, and leave the "if this or that is null, else ..." in operator overloads to the framework.
The only tricky thing about overriding operator overloads is that you can no longer use those operators in your Equals implementation, for example to compare with
null
. Instead,object.ReferenceEquals
can be used to achieve the same effect.Following the TwoDPoint example in the MSDN Guidelines for Overriding Equals() and Operator == article, this is the pattern I generate when implementing value equality for types:
The form above is the safest implementation, as it simply forwards the field equality checks to the framework and requires no knowledge of whether the fields overload the equality operators. It is perfectly fine to simplify this where you know the overload exists:
You can also replace the
EqualityComparer<T>
calls in the operator overloads with calls to the staticobject.Equals
method when comparing reference types, or when boxing value types does not matter:See also What is the best algorithm for an overridden GetHashCode? for implementing
GetHashCode
.Other answers give good solutions to the general problem.
However, your own code can be simplified into a relatively simple solution ...
Firstly, at the start of your
==
operator you have this:This qualifies as "working too hard".
If
ClauseBE
is a reference type, then you only need to compare withnull
- the "as object
" is redundant; equally, ifClauseBE
is a value type, then it can never benull
.Assuming that
ClauseBE
is a reference type (the most likely case), then you can simplify to this - note that we useObject.Equals()
to avoid infinite recursion and a stack blowout.One useful shortcut is to use
Object.ReferenceEquals()
- which handles nulls for you.So you could write this instead:
with the bonus that this also handles the case where
a
andb
are the same exact object.Once you get past the
Object.ReferenceEquals()
test, you know thata
andb
are different.So your next test:
can be simplified - since you know that if
a
is null,b
cannot be null, and so on.If this test fails, then you know that
a
andb
are different, and that neither is null. A good time to call your overriddenEquals()
.