I find my self overriding Equals()
and GetHashCode()
frequently to implement the semantic that business objects with identical property values are equal. That leads to code that is repetitive to write and fragile to maintain (property gets added and one/both of the overrides are not updated).
The code ends up looking something like this (comments on the implementation are welcome):
public override bool Equals(object obj)
{
if (object.ReferenceEquals(this, obj)) return true;
MyDerived other = obj as MyDerived;
if (other == null) return false;
bool baseEquals = base.Equals((MyBase)other);
return (baseEquals &&
this.MyIntProp == other.MyIntProp &&
this.MyStringProp == other.MyStringProp &&
this.MyCollectionProp.IsEquivalentTo(other.MyCollectionProp) && // See http://stackoverflow.com/a/9658866/141172
this.MyContainedClass.Equals(other.MyContainedClass));
}
public override int GetHashCode()
{
int hashOfMyCollectionProp = 0;
// http://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/
// BUT... is it worth the extra math given that elem.GetHashCode() should be well-distributed?
int bitSpreader = 31;
foreach (var elem in MyCollectionProp)
{
hashOfMyCollectionProp = spreader * elem.GetHashCode();
bitSpreader *= 31;
}
return base.GetHashCode() ^ // ^ is a good combiner IF the combined values are well distributed
MyIntProp.GetHashCode() ^
(MyStringProp == null ? 0 : MyStringProp.GetHashValue()) ^
(MyContainedClass == null ? 0 : MyContainedClass.GetHashValue()) ^
hashOfMyCollectionProp;
}
My Questions
- Is the implementation pattern sound?
- Is ^ adequate given that the contributing component values are well-distributed? Do I need to multiply by 31-to-the-N when combining collection elements given their hash is well distributed?
- It seems this code could be abstracted into code that uses reflection to determine public properties, builds an expression tree that matches the hand-coded solution, and executes the expression tree as needed. Does that approach seem reasonable? Is there an existing implementation somewhere?