What I like about software development is that after developing software for sixteen years, I can still learn new things. Sometimes, in fact, like this morning, I can learn new things about topics that I thought that I’ve understood in the past, only to find out what I was wrong. I honestly believe that to be a good learner, you need to accept that not everything that you do is correct, and that sometimes you thought they were correct given your understanding of the information available, which in this case is accurate.
This morning, driven by some information that I noticed in the Objective-C documentation for the NSObject class, I decided to do some digging. In earlier posts about NSObject, I was looking at some of the similarities to the .NET System.Object class and the Java root Object class. Specifically, I was looking at the Equals and GetHashCode methods in .NET and their similar constructs in Java and Objective-C. The problem lay with the statements in the Objective-C documentation that said (summarized) an object should give back the same hash code each time and that if two objects are equal, then they should have the same hash code.
The issue for me is that I’ve struggled with implementing Object.GetHashCode in .NET. I understand its use in dictionaries and hash tables, but it’s been the implementation that’s been problematic. The dilemma has always been if my hash code is based on the properties of my object, and the value of one of the properties changes, the hash code is going to be different. To get around that, I’ve always based my hash codes on immutable values such as the unique identifier for a persistent object.
I’ve frequently implemented the Equals method and implemented the IEquatable interface in .NET to allow comparisons between two different instances of the same object class. By overriding the Object.Equals method, this has forced me to basically override Object.GetHashCode as well.
In researching the semantics of implementing Object.Equals and Object.GetHashCode (and the similar constructs in the other languages), I got an idea to look back at the Framework Design Guidelines (second edition) book. The answer was found in section 8.9.1.2 on page 269. At the bottom, in the section titled “Equals on Reference types” the guidance states the following:
- Override Equals to provide value equality if the class type represents a value.
- Do not implement value equality on mutable class types.
The first rule means that if you implement an immutable type that represents a value (for example, System.String), then override the Object.Equals method to implement value equality. If you have a class type with properties that can be changed (such as an object representing a persistent data record or a data bound data source), then do not implement value equality and do not, therefore, override Equals or GetHashCode.
So the moral of this story is only override Equals and GetHashCode on immutable objects. This would apply also the Objective-C and Java as well.
Lesson learned. Now why couldn’t they have put this more clearly in the .NET reference documentation?