Monday, July 21, 2014

Linq Distinct - Override Equals and GetHashCode

Recently in our code base I ran across a portion of code that was selecting out a distinct set of objects. The Equals method had been overridden to provide equality based on values of the object, and was being used to compare object equality between objects in a list. If the objects proved to be equal, the first instance was transferred into a new list. There was a comment beside this code that said something like "Check into using LINQ Distinct for this"...

After setting up a unit test and overriding the equals method, I noted that I could not get my tests to pass.  I turned to the MSDN docs for help. The issue was the GetHashCode method must also be overridden. The idea behind this is that when doing an equality comparison GetHashCode, and Equals are both used by GenericEqualityComparer<T>.   GetHasCode is used to help determine if an object is possibly equal, if it is then the Equals method will be called to determine absolute equality. If your object is mutable, getting a hash code from it can be impractical.  In this case returning 1 as the hash code is probably the best thing though it will affect look-up performance.

Once this was understood then there comes the more nuanced issue of choosing to implement IEquatable<T>. If you don't implement this interface, methods in the BCL that use the GenericEqualityComparer<T> will still function, so what's the point? Once again the MSDN docs explain how things work.

"For a value type, you should always implement IEquatable(Of T) and override Object.Equals(Object) for better performance. Object.Equals boxes value types and relies on reflection to compare two values for equality. Both your implementation of Equals and your override of Object.Equals should return consistent results.
If you implement IEquatable(Of T), you should also implement IComparable(Of T) if instances of your type can be ordered or sorted. If your type implements IComparable(Of T), you should also always implement IEquatable(Of T)."

A few more things to think about...  Don't forget to override the equality and inequality operators to keep object behavior consistent.  If your overriding Equals, you may want to think about sealing your class, to keep inherited classes from messing up on an Equals implementation.

No comments:

Post a Comment