Creating equatable objects

Immutable value types should override Object.Equals to get better performance than the default implementation, which uses reflection, and should override Object.GetHashCode to avoid a bug in the default implementation. Immutable reference types should override Equals if they need to provide value equality semantics. (For example, String is a reference type, but provides value equality: two strings are equal if they have the same content, not just if they are the same object in memory.)

There are a lot of rules to follow to implement equality correctly. A concise list is presented in Framework Design Guidelines, 2nd Edition, sections 8.9.1 and 8.9.2. A detailed explanation of the rules can also be found in the documentation for Object.Equals, Implementing the Equals Method, and Guidelines for Implementing Equals and the Equality Operator(==).

Because it’s sometimes easier to see (and copy!) a reference implementation than to just read a list of rules, we’ve made sample EquatableClass and EquatableStruct implementations available.

Both types:

  • Implement IEquatable<T>.Equals and place the primary equality comparison in this method.
  • Override Object.Equals and implement the equality & inequality operators; these should all delegate to IEquatable<T>.Equals.
  • Override Object.GetHashCode and provide an implementation that is consistent with Equals.
  • Never throw an exception from any of these methods.

EquatableClass:

  • Perform null checks before using another instance of this class.
  • Optional: Use Object.ReferenceEquals to check for null, because == null will needlessly use the overloaded operator.

EquatableStruct:

  • Null checks are unnecessary (except when overriding Object.Equals) because value types can’t be null.

Our reference implementations also use helper methods from HashCodeUtility and ObjectImpl in order to simplify the implementation of GetHashCode and the overloaded operators.

Posted by Bradley Grainger on February 08, 2010