When (not) to override Equals?
One unexpected effect of overriding Equals is that if you do it, you should also override GetHashCode, if only because the Hashtable implementation relies on both being in sync for the objects used as the keys.
Your implementation should respect three rules:
- Two objects for which Equals returns true should have the same hash code.
- The hashcode distribution for instances of a class should be random.
- If you get a hash code for an object and modify the object's properties, the hash code should remain the same (just as the song).
While the first requirement ensures consistency if your class instances are used as the key in a hashtable, the second ensures good performance of the hashtable.
The third requirement has an annoying consequence: the properties that you use to compute the hash must be immutable (ie, they must be set from a constructor only and be impossible to set at any time after that).
So what should you do if your Equals implementation involves mutable properties? Well, you could exclude these from the computation of the hash and only take into account the immutable ones, but doing so, you're destroying requirement number 2.
The answer is that you should actually never override Equals on a mutable type. You should instead create a ContentsEquals (or whatever name you may choose) method to compare the instances and leave Equals do its default reference comparison. Don't touch GetHashCode in this case.
The third requirement has an annoying consequence: the properties that you use to compute the hash must be immutable (ie, they must be set from a constructor only and be impossible to set at any time after that).
So what should you do if your Equals implementation involves mutable properties? Well, you could exclude these from the computation of the hash and only take into account the immutable ones, but doing so, you're destroying requirement number 2.
The answer is that you should actually never override Equals on a mutable type. You should instead create a ContentsEquals (or whatever name you may choose) method to compare the instances and leave Equals do its default reference comparison. Don't touch GetHashCode in this case.
Update: It may seem reasonable to say that it's ok to override Equals and GetHashCode on mutable objects if you document clearly that once the object has been used as the key in a hashtable, it should not be changed and that if it is, unpredictable things can happen. The problem with that, though, is that it's not easily discoverable (documentation only). Thus, it is better to avoid overriding them altogether on mutable objects.