【问题标题】:Simplify Overriding Equals(), GetHashCode() in C# for Better Maintainability在 C# 中简化覆盖 Equals()、GetHashCode() 以获得更好的可维护性
【发布时间】:2012-03-14 18:29:10
【问题描述】:

我发现自己经常重写 Equals()GetHashCode() 以实现具有相同属性值的业务对象相等的语义。这会导致代码重复编写且维护脆弱(添加了属性并且其中一个/两个覆盖没有更新)。

代码最终看起来像这样(欢迎使用 cmets 实现):

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

【问题讨论】:

  • 为什么投反对票(发布后一年多)?这个问题非常合理。如果有什么问题,请说出来。

标签: c# equals hashcode maintainability


【解决方案1】:

MSDN 实际上没有说“不要为可变类型重载 Equals 等”。以前是这么说的,现在说:

当你定义一个类或结构时,你决定它是否有意义 为值相等(或等价)创建自定义定义 方式。通常,当 类型应该被添加到某种集合中,或者当 它们的主要目的是存储一组字段或属性。

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

尽管如此,当对象参与散列集合(Dictionary<T,U>HashSet<T> 等)时,散列码的稳定性仍然很复杂。

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

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

【讨论】:

    【解决方案2】:

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

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

    鉴于贡献的组件值分布良好,^ 是否足够?

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

    【讨论】:

    • MSDN 哪里说的?我试着用谷歌搜索文字,只找到了这个问题。
    • 另外...举一个简单的例子来说明我在哪里看到了覆盖 Equals 的实用程序(但也许我遗漏了一些东西),你将如何实现一个不覆盖 Equals 的单元测试给定@987654323 @?
    • 关于 ^... 在我的例子中,每个 XORed 值都是 GetHashCode() 的结果,应该合理分配。实施后,您是否发现我使用 ^ 的方式存在缺陷?即使每个集合元素的哈希值都是 GetHashCode() 的结果,您是否会将集合元素乘以素数?
    • 它曾经在this page 上拼写,但现在只在脚注中(有人引用旧文本)。您仍然可以从有关值/引用类型的各种建议中提炼出来。
    • 关于^ :您有多少int 属性?它们是它们自己的 HashCode,并且往往分布良好。搜索此网站。
    【解决方案3】:

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

    所以

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

    应该是

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-04-09
      • 1970-01-01
      • 2012-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多