简体   繁体   English

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

[英]Simplify Overriding Equals(), GetHashCode() in C# for Better Maintainability

I find my self overriding Equals() and GetHashCode() frequently to implement the semantic that business objects with identical property values are equal. 我经常发现我的自我重写Equals()GetHashCode()来实现具有相同属性值的业务对象相等的语义。 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 我的问题

  1. Is the implementation pattern sound? 实施模式是否合理?
  2. 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? 在组合集合元素时,我需要乘以31到N,因为它们的散列分布均匀吗?
  3. 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? 某处有现有的实施吗?

MSDN actually doesn't say "don't overload Equals et al for mutable types". MSDN实际上并没有说“不要为可变类型重载Equals等”。 It used to say that, but now it says: 它曾经说过,但现在它说:

When you define a class or struct, you decide whether it makes sense to create a custom definition of value equality (or equivalence) for the type. 定义类或结构时,您可以决定是否为类型创建值相等(或等效)的自定义定义。 Typically, you implement value equality when objects of the type are expected to be added to a collection of some sort, or when their primary purpose is to store a set of fields or properties. 通常,当期望将类型的对象添加到某种类型的集合时,或者当它们的主要目的是存储一组字段或属性时,实现值相等。

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

Still, there are complexities surrounding stability of the hash code while an object participates in a hashed collection ( Dictionary<T,U> , HashSet<T> , etc.). 然而,当对象参与散列集合( Dictionary<T,U>HashSet<T>等)时,存在围绕哈希码的稳定性的复杂性。

I decided to opt for the best of both worlds, as outlined here: 我决定选择两全其美,如下所述:

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

I find my self overriding Equals() and GetHashCode() frequently 我发现我的自我经常覆盖Equals()和GetHashCode()

  • MSDN says : don't overload Equals et al for mutable types MSDN说:不要为Equals等过载可变类型

Is ^ adequate given that the contributing component values are well-distributed? 鉴于贡献组件值分布均匀,是否足够?

  • Yes, but hey are not always well distributed. 是的,但嘿并不总是分布很好。 Consider int properties. 考虑int属性。 Shifting with some (small) prime numbers is advised. 建议使用一些(小)素数进行移位。

Perhaps I'm confused here, but shouldn't the on null check return a 1 instead of a 0 in the GetHashCode override? 也许我在这里很困惑,但是在GetHashCode覆盖中,on null check不应该返回1而不是0?

So 所以

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

should be 应该

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM