GetHashCode Guidelines in C#

Viewed 58539

I read in the Essential C# 3.0 and .NET 3.5 book that:

GetHashCode()’s returns over the life of a particular object should be constant (the same value), even if the object’s data changes. In many cases, you should cache the method return to enforce this.

Is this a valid guideline?

I have tried a couple built-in types in .NET and they didn't behave like this.

9 Answers

The answer is mostly, it is a valid guideline, but perhaps not a valid rule. It also doesn't tell the whole story.

The point being made is that for mutable types, you cannot base the hash code on the mutable data because two equal objects must return the same hash code and the hash code has to be valid for the lifetime of the object. If the hash code changes, you end up with an object that gets lost in a hashed collection because it no longer lives in the correct hash bin.

For example, object A returns hash of 1. So, it goes in bin 1 of the hash table. Then you change object A such that it returns a hash of 2. When a hash table goes looking for it, it looks in bin 2 and can't find it - the object is orphaned in bin 1. This is why the hash code must not change for the lifetime of the object, and just one reason why writing GetHashCode implementations is a pain in the butt.

Update
Eric Lippert has posted a blog that gives excellent information on GetHashCode.

Additional Update
I've made a couple of changes above:

  1. I made a distinction between guideline and rule.
  2. I struck through "for the lifetime of the object".

A guideline is just a guide, not a rule. In reality, GetHashCode only has to follow these guidelines when things expect the object to follow the guidelines, such as when it is being stored in a hash table. If you never intend to use your objects in hash tables (or anything else that relies on the rules of GetHashCode), your implementation doesn't need to follow the guidelines.

When you see "for the lifetime of the object", you should read "for the time the object needs to co-operate with hash tables" or similar. Like most things, GetHashCode is about knowing when to break the rules.

From MSDN

If two objects compare as equal, the GetHashCode method for each object must return the same value. However, if two objects do not compare as equal, the GetHashCode methods for the two object do not have to return different values.

The GetHashCode method for an object must consistently return the same hash code as long as there is no modification to the object state that determines the return value of the object's Equals method. Note that this is true only for the current execution of an application, and that a different hash code can be returned if the application is run again.

For the best performance, a hash function must generate a random distribution for all input.

This means that if the value(s) of the object change, the hash code should change. For example, a "Person" class with the "Name" property set to "Tom" should have one hash code, and a different code if you change the name to "Jerry". Otherwise, Tom == Jerry, which is probably not what you would have intended.


Edit:

Also from MSDN:

Derived classes that override GetHashCode must also override Equals to guarantee that two objects considered equal have the same hash code; otherwise, the Hashtable type might not work correctly.

From MSDN's hashtable entry:

Key objects must be immutable as long as they are used as keys in the Hashtable.

The way I read this is that mutable objects should return different hashcodes as their values change, unless they are designed for use in a hashtable.

In the example of System.Drawing.Point, the object is mutable, and does return a different hashcode when the X or Y value changes. This would make it a poor candidate to be used as-is in a hashtable.

I think that the documentation regarding GetHashcode is a bit confusing.

On one hand, MSDN states that the hashcode of an object should never change , and be constant On the other hand, MSDN also states that the return value of GetHashcode should be equal for 2 objects, if those 2 objects are considered to be equal.

MSDN:

A hash function must have the following properties:

  • If two objects compare as equal, the GetHashCode method for each object must return the same value. However, if two objects do not compare as equal, the GetHashCode methods for the two object do not have to return different values.
  • The GetHashCode method for an object must consistently return the same hash code as long as there is no modification to the object state that determines the return value of the object's Equals method. Note that this is true only for the current execution of an application, and that a different hash code can be returned if the application is run again.
  • For the best performance, a hash function must generate a random distribution for all input.

Then, this means that all your objects should be immutable, or the GetHashcode method should be based on properties of your object that are immutable. Suppose for instance that you have this class (naive implementation):

public class SomeThing
{
      public string Name {get; set;}

      public override GetHashCode()
      {
          return Name.GetHashcode();
      }

      public override Equals(object other)
      {
           SomeThing = other as Something;
           if( other == null ) return false;
           return this.Name == other.Name;
      }
}

This implementation already violates the rules that can be found in MSDN. Suppose you have 2 instances of this class; the Name property of instance1 is set to 'Pol', and the Name property of instance2 is set to 'Piet'. Both instances return a different hashcode, and they're also not equal. Now, suppose that I change the Name of instance2 to 'Pol', then, according to my Equals method, both instances should be equal, and according to one of the rules of MSDN, they should return the same hashcode.
However, this cannot be done, since the hashcode of instance2 will change, and MSDN states that this is not allowed.

Then, if you have an entity, you could maybe implement the hashcode so that it uses the 'primary identifier' of that entity, which is maybe ideally a surrogate key, or an immutable property. If you have a value object, you can implement the Hashcode so that it uses the 'properties' of that value object. Those properties make up the 'definition' of the value object. This is of course the nature of a value object; you're not interested in it's identity, but rather in it's value.
And, therefore, value objects should be immutable. (Just like they are in the .NET framework, string, Date, etc... are all immutable objects).

Another thing that comes in mind:
During which 'session' (I don't know really how I should call this) should 'GetHashCode' return a constant value. Suppose you open up your application, load an instance of an object out of the DB (an entity), and get its hashcode. It will return a certain number. Close the application, and load the same entity. Is it required that the hashcode this time has the same value as when you loaded the entity the first time ? IMHO, not.

This is good advice. Here's what Brian Pepin has to say on the matter:

This has tripped me up more than once: Make sure GetHashCode always returns the same value across the lifetime of an instance. Remember that hash codes are used to identify "buckets" in most hashtable implementations. If an object's "bucket" changes, a hashtable may not be able to find your object. These can be very hard bugs to find, so get it right the first time.

Not directly answering your question, but - if you use Resharper, do not forget it has a feature that generates a reasonable GetHashCode implementation (as well as the Equals method) for you. You can of course specify which members of the class will be taken into account when computing the hashcode.

Check out this blog post from Marc Brooks:

VTOs, RTOs and GetHashCode() -- oh, my!

And then check out the follow up post (can't link as I'm new, but there's a link in the initlal article) which discusses further and covers some minor weaknesses in the initial implementation.

This was everything I needed to know about creating a GetHashCode() implementation, he even provides a download of his method along with some other utilities, in short gold.

The hashcode never changes, but it's also important to understand where the Hashcode is coming from.

If your object is using value semantics, i.e. the object's identity is defined by its values (like String, Color, all structs). If your object's identity is independent of all of its values, then the Hashcode is identified by a subset of its values. For example, your StackOverflow entry is stored in a database somewhere. If you change your name or email, your customer entry stays the same, although some values have changed (ultimately you're usually identified by some long customer id #).

So in short:

Value type semantics - Hashcode is defined by values Reference type semantics - Hashcode is defined by some id

I suggest you read Domain Driven Design by Eric Evans, where he goes into entities vs value types (which is more or less what I attempted to do above) if this still doesn't make sense.

Related