【问题标题】:IEqualityComparer GetHashCode being called but Equals notIEqualityComparer GetHashCode 被调用但不等于
【发布时间】:2014-03-17 11:23:38
【问题描述】:

我有两个要比较的列表。所以我创建了一个实现IEqualityComparer 接口的类,请参见下面的代码底部部分。

当我单步执行我的代码时,代码会通过我的 GetHashCode 实现而不是 Equals?我并不真正了解GetHashCode 方法,尽管在互联网上阅读以及它到底在做什么。

List<FactorPayoffs> missingfactorPayoffList = 
    factorPayoffList.Except(
        factorPayoffListOrg,
        new FactorPayoffs.Comparer()).ToList();

List<FactorPayoffs> missingfactorPayoffListOrg =
    factorPayoffListOrg.Except(
        factorPayoffList,
        new FactorPayoffs.Comparer()).ToList();

所以在上面的两行代码中,两个列表都返回给我每个项目,告诉我这两个列表不包含任何相同的项目。这不是真的,只有不同的行。我猜这是因为 Equals 方法没有被调用,这反过来让我想知道我的 GetHashCode 方法是否按预期工作?

class FactorPayoffs
    {
        public string FactorGroup { get; set; }
        public string Factor { get; set; }
        public DateTime dtPrice { get; set; }
        public DateTime dtPrice_e { get; set; }
        public double Ret_USD { get; set; }

        public class Comparer : IEqualityComparer<FactorPayoffs>
        {
            public bool Equals(FactorPayoffs x, FactorPayoffs y)
            {                    
                return x.dtPrice == y.dtPrice && 
                    x.dtPrice_e == y.dtPrice_e && 
                    x.Factor == y.Factor && 
                    x.FactorGroup == y.FactorGroup;
            }

            public int GetHashCode(FactorPayoffs obj)
            {
                int hash = 17;
                hash = hash * 23 + (obj.dtPrice).GetHashCode();
                hash = hash * 23 + (obj.dtPrice_e).GetHashCode();
                hash = hash * 23 + (obj.Factor ?? "").GetHashCode();
                hash = hash * 23 + (obj.FactorGroup ?? "").GetHashCode();
                hash = hash * 23 + (obj.Ret_USD).GetHashCode();
                return hash;
            }
        }
    }

【问题讨论】:

  • 当两个实例被认为相等时,GetHashCode 实现必须返回相同的值。当它们不相等时,它应该尝试返回不同的数字,但这并不重要。

标签: c#


【解决方案1】:

您的EqualsGetHashCode 实现应该包含完全相同的一组属性;他们没有。

在更正式的术语中,GetHashCode 必须始终为两个比较相等的对象返回相同的值。使用您当前的代码,仅在 Ret_USD 值上不同的两个对象将始终比较相等,但不能保证具有相同的哈希码。

所以发生的情况是 LINQ 对您认为相等的两个对象调用 GetHashCode,返回不同的值,得出结论,由于值不同,对象不能相等,因此调用 Equals 并移动开。

要解决此问题,要么从 GetHashCode 中删除 Ret_USD 因子,要么在 Equals 中引入它(只要对你的相等语义有意义)。

【讨论】:

【解决方案2】:

GetHashCode 旨在作为对相等性的快速但粗略的估计,因此许多可能涉及大量比较的操作从检查此结果而不是Equals 开始,并且仅在必要时使用Equals。特别是如果x.GetHashCode()!=y.GetHashCode(),那么我们已经知道x.Equals(y)是假的,所以没有理由调用Equals。如果有x.GetHashCode()==y.GetHashCode(),那么x可能等于y,但只有调用Equals才能给出明确的答案。

如果您实现GetHashCode 的方式导致GetHashCode 对于Equals 返回true 的两个对象不同,那么您的代码和许多依赖这些方法的集合类和算法中存在错误会默默地失败。

【讨论】:

    【解决方案3】:

    如果你想强制执行 Equals 你可以如下实现它

    public int GetHashCode(FactorPayoffs obj) {
            return 1;
        }
    

    【讨论】:

      【解决方案4】:

      像这样重写GetHashCode 实现,以匹配Equals 实现的语义。

      public int GetHashCode(FactorPayoffs obj)
      {
          unchecked
          {
                  int hash = 17;
                  hash = hash * 23 + obj.dtPrice.GetHashCode();
                  hash = hash * 23 + obj.dtPrice_e.GetHashCode();
                  if (obj.Factor != null)
                  {
                      hash = hash * 23 + obj.Factor.GetHashCode();
                  }
      
                  if (obj.FactorGroup != null)
                  {
                      hash = hash * 23 + obj.FactorGroup.GetHashCode();
                  }
      
                  return hash;
          }
      }
      

      注意,你应该使用unchecked,因为你不关心溢出。另外,合并到string.Empty 是毫无意义的浪费,只需从哈希中排除即可。

      here for the best generic answer I know

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-08-20
        • 2012-10-23
        • 1970-01-01
        • 2017-05-20
        • 1970-01-01
        • 1970-01-01
        • 2014-11-08
        相关资源
        最近更新 更多