We already discussed implementing a Value Object's Equals
here and here.
Now it is time to look at GetHashCode
.
When Should You Override GetHashCode
?
Each time you override a class's Equals
method, the compiler will warn you when you didn't override the GetHashCode
method. But why? Where is GetHashCode
used?
The answer to that is pretty simple. When you insert a key-value pair in a dictionary/hashtable, .NET uses the GetHashCode
of the key to generate an index for the internal array uses in a hashtable (which Dictionary<K, V>
also uses). That same GetHashCode
is also used to retrieve the value with that (same) key.
Rule #1: Equals means GetHashCode
equals
This means that if two key values are equal then the GetHashCode
should also be equal. Otherwise, the hashtable will not be able to retrieve its value.
Rule #2: Hashtable keys should be immutable
If you use a certain instance as a key in a hashtable, and then you change (mutate) that instance, the hashtable will not be able to retrieve that key-pair again! So make sure you only use immutable types as a key for a dictionary. Domain-Driven Design Value Objects are ideal for that!
Implementing GetHashCode
Implementing GetHashCode
used to be pretty hard to implement manually because you need to take care of a bunch of things. First of all, there is rule #1. And rule #2. A good implementation also returns integer values from the full range of int
, not just 1,2,3,... because that results in an inefficient hashtable.
Today writing GetHashCode
is easy, thanks to the new HashCode
class available in .NET Core.
For example:
public override int GetHashCode()
{
var hash = new HashCode();
hash.Add(this.Price);
hash.Add(this.When);
return hash.ToHashCode();
}
Do take care that you add all properties used in the Equals
method (rule #1).
If you're using the U2U.ValueObjectComparers package, life is even easier:
public override int GetHashCode()
=> ValueObjectComparer<MyValueObject>.Instance.GetHashCode(this);
Implementation
The full implementation can be found in my repo.
Just like in implementing Equals I have used "Just Once" reflection to generate a hasher
method that internally uses the HashCode
class. Then the GetHashCode
method simply calls that hasher.
public override int GetHashCode()
=> ValueObjectComparer<YourTypeHere>.Instance.GetHashCode(this);
The GenerateHasher
dynamically generates this kind of code:
var hash = new HashCode();
hash.Add(this.Price);
hash.Add(this.When);
return hash.ToHashCode();
With Expression
this looks like this:
ParameterExpression obj = Expression.Parameter(typeof(T), "obj");
ParameterExpression hashCode = Expression.Variable(hashCodeType, "hashCode");
BlockExpression block = Expression.Block(
type: typeof(int),
variables: new ParameterExpression[] { hashCode },
expressions: new Expression[]
{
Expression.Assign(hashCode, Expression.New(hashCodeType)),
Expression.Block(GenerateAddToHashCodeExpressions()),
Expression.Call(hashCode, hashCodeMethod)
});
The GenerateAddToHashCodeExpressions
nested method takes care of calling the Add
method for each property:
Expression[] GenerateAddToHashCodeExpressions()
{
List<Expression> adders = new List<Expression>();
foreach (PropertyInfo propInfo in typeof(T)
.GetProperties(BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public))
{
if (propInfo.IsDefined(typeof(IgnoreAttribute)))
{
continue;
}
MethodInfo boundAddMethod = addMethod.MakeGenericMethod(propInfo.PropertyType);
adders.Add(Expression.Call(hashCode, boundAddMethod, Expression.Property(obj, propInfo)));
}
return adders.ToArray();
}
I then wrap the whole thing in a Lambda
expression and Compile
it:
Func<T, int> hasher = Expression.Lambda<Func<T, int>>(block, obj).Compile();
return hasher;