tags:

views:

382

answers:

4

Below is a sample implementation of overriding Object.Equals() for an entity base class from which all other entities in an application derive.

All entity classes have the property Id, which is a nullable int. (It's the primary key of whatever table the entity class corresponds to.)

public override bool Equals(object obj)
        {
            if (obj == null || GetType() != obj.GetType())
                return false;

            if (base.Equals(obj))
                return true;

            return Id.HasValue && ((EntityBase) obj).Id.HasValue &&
                   Id.Value == ((EntityBase) obj).Id.Value;
        }

Given this implementation of Equals(), how do you correctly implement GetHashCode()?

+1  A: 

You can only correctly implement GetHashCode() if the Id property is immutable for the lifetime of an instance (or at least for the time that its hash needs to be used, such as while the object is in a map or other collection requiring the hash).

Assuming that it is, you can just use the value of Id as the hash for all valid values and then use a fixed hash for null. I can't remember what the most appropriate is for this, but I would assume a randomly selected value for null(randomly selected prior to compilation, not at runtime) or the median value of valid Id values (i.e. halfway between 0 and int.Max).

Jeff Yates
It doesn't have to be immutable for the lifetime - only from the point at which it's first inserted into a map or whatever. It's okay to create an instance, mess around with it, put it into a map and *then* stop messing with it.
Jon Skeet
That's a fair point. I'll make an edit.
Jeff Yates
+1  A: 

This article might steer you in the right direction.

davogones
+8  A: 

I'd implement it as:

public override int GetHashCode()
{
    int hash = 37;
    hash = hash * 23 + base.GetHashCode();
    hash = hash * 23 + Id.GetHashCode();
    return hash;
}

A null value of Id will return 0 for Id.GetHashCode().

If your class just derives from Object, I'd just return Id.GetHashCode()

Note that your equality definition won't return true if neither entity has an Id, but the same hashcode will be returned from both objects. You may wish to consider changing your Equals implementation.

Jon Skeet
For those wondering, like I did: The 23 and 37 are arbitrary numbers which are co-prime. Jon stated this in a similar answer here: http://www.eggheadcafe.com/software/aspnet/29483139/override-gethashcode.aspx
Jeff Yates
A: 

What Jon Skeet answered is a good solution, however, you might want to add an unchecked code block to allow integer overflowing

unchecked
{
  int hash = ...;
  return hash
}

http://msdn.microsoft.com/en-us/library/a569z7k8%28VS.71%29.aspx

I'd also like to add, again, that using base.GetHashCode() on POCO's will call the default object.GetHashCode. That's definitely not what you want...

Jaap