IEqualityComparer正在调用GetHashCode,但不是Equals

时间:2014-03-17 11:23:38

标签: c#

我想要比较两个列表。所以我创建了一个实现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;
            }
        }
    }

4 个答案:

答案 0 :(得分:23)

您的EqualsGetHashCode实施应涉及完全相同的属性集;他们没有。

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

所以会发生什么是LINQ在你认为相等的两个对象上调用GetHashCode,得到不同的值,得出的结论是,由于值不同,对象不能相等所以在调用{{1}时根本就没有意义继续前进。

要解决此问题,请从Equals中移除Ret_USD因子,或在GetHashCode内引入它(对于您的相等语义而言,这是有意义的。)

答案 1 :(得分:12)

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

如果您以GetHashCode返回Equals的两个对象导致true不同的方式实施{{1}},那么您的代码中存在错误依赖这些方法的集合类和算法将无声地失败。

答案 2 :(得分:6)

如果要强制执行Equals,可以按如下方式实现

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

答案 3 :(得分:2)

重写这样的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