在C#中简化覆盖等于(),GetHashCode()以获得更好的可维护性

我经常发现我的自我重写Equals()GetHashCode()来实现具有相同属性值的业务对象相等的语义。 这会导致代码重复写入并且易于维护(属性被添加,并且一个/两个覆盖都不会更新)。

代码最终看起来像这样(欢迎对实现的评论):

 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; } 

我的问题

  1. 实施模式是否合理?
  2. 鉴于贡献组件值分布均匀,是否足够? 在组合集合元素时,我需要乘以31到N,因为它们的散列分布很好吗?
  3. 似乎可以将此代码抽象为使用reflection来确定公共属性的代码,构建与手动编码解决方案匹配的表达式树,并根据需要执行表达式树。 这种方法看起来合理吗? 某处是否存在现有实施?

MSDN实际上没有说“不要为可变类型重载Equals等”。 过去常说,但现在它说:

定义类或结构时,您可以决定为类型创建值相等(或等效)的自定义定义是否有意义。 通常,当期望将类型的对象添加到某种类型的集合时,或者当它们的主要目的是存储一组字段或属性时,实现值相等。

http://msdn.microsoft.com/en-us/library/dd183755.aspx

尽管如此,当对象参与散列集合( DictionaryHashSet等)时,哈希码的稳定性仍存在复杂性。

我决定选择两全其美,如下所述:

https://stackoverflow.com/a/9752155/141172

我发现我的自我经常覆盖Equals()和GetHashCode()

  • MSDN说:不要为可变类型重载Equals等

鉴于贡献组件值分布均匀,是否足够?

  • 是的,但嘿并不总是分布很好。 考虑int属性。 建议使用一些(小)素数进行移位。

也许我在这里很困惑,但是在GetHashCode覆盖中,on null check不应该返回1而不是0?

所以

 MyStringProp == null ? 0 : MyStringProp.GetHashValue() 

应该

 MyStringProp == null ? 1 : MyStringProp.GetHashValue()