- If you want custom equality logic, whatever else you might do, override Object.Equals. This method is used by the various data structures in System.Collections (and elsewhere in the BCL) to determine equality. It's the first best way to do equality.
- IEquatable<T> should be implemented by structs with custom equality. Because calling Object.Equals on a struct involves boxing, and the implementation for value types uses reflection (!!!!!), IEquatable<T> gets around both of those problems. When implementing IEquatable<T>, always override Object.Equals as well.
- Operator overloading is a little bit tricky because it's really just a compiler feature. The compiler has a lookup rule for the operator implementation which takes the most derived types along the operands' type ancestry. Whereas Object.Equals is a virtual method whose overridden implementation will always be used, overloaded operators will only be used if both operands are of static (compile-time) types that are or derive from the types specified in the overload. Overloading operators is a matter of discretion. It's more commonly done with value than reference types. If you overload the equality operators, also override Equals (an implement IEquatable<T> if the type is a struct).
When you are just overriding Equals, here is the pattern I find works the best:
public override bool Equals (object obj)
{
MyRefType mine = obj as MyRefType;
return mine != null && /* custom equality logic here */;
}
If you want to overload operators and it's a reference type, here's the thing to do:
public override bool Equals (object obj)
{
MyRefType mine = obj as MyRefType;
return mine == this;
}
public static bool operator == (MyRefType mine1, MyRefType mine2)
{
if (Object.ReferenceEquals (mine1, null)) {
return Object.ReferenceEquals (mine2, null);
}
return !Object.ReferenceEquals (mine2, null) &&
/* custom equality logic here /*;
}
public static bool operator != (MyRefType mine1, MyRefType mine2)
{
return !(mine1 == mine2);
}
If you have a value type, here's the scenario without overriding the operators.
public override bool Equals (object obj)
{
return obj is MyValueType && Equals ((MyValueType)obj);
}
public bool Equals (MyValueType mine)
{
return /* custom equality logic here */
}
And here's the value type with operator overloads
public override bool Equals (object obj)
{
return obj is MyValueType && Equals ((MyValueType)obj);
}
public bool Equals (MyValueType mine)
{
return mine == this;
}
public static bool operator == (MyType mine1, MyType mine2)
{
return /* custom equality logic here */
}
public static bool operator != (MyType mine1, MyType mine2)
{
return !(mine1 == mine2);
}
Want to share your tips for equality? Have a better pattern? Leave a comment!