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

时间:2012-03-14 18:29:10

标签: c# equals hashcode maintainability

我经常发现我的自我重写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. 似乎可以将此代码抽象为使用反射来确定公共属性的代码,构建与手动编码解决方案匹配的表达式树,并根据需要执行表达式树。这种方法看起来合理吗?某处是否存在现有实施?

3 个答案:

答案 0 :(得分:4)

MSDN实际上说“不要为可变类型重载Equals等”。它曾经说过,但现在却说:

  

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

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

但是,当一个对象参与散列集合(Dictionary<T,U>HashSet<T>等)时,哈希码的稳定性仍然存在复杂性。

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

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

答案 1 :(得分:1)

  

我经常发现我的自我重写Equals()和GetHashCode()

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

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

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

答案 2 :(得分:0)

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

所以

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

应该是

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