调用了等价比较器的GetHashCode,但是没有调用

本文关键字:调用 比较器 GetHashCode | 更新日期: 2023-09-27 17:50:32

我有两个列表,我试图比较。因此,我创建了一个实现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,但是没有调用

您的EqualsGetHashCode实现应该包含完全相同的一组属性;

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

所以发生的是LINQ在两个你认为相等的对象上调用GetHashCode,得到不同的值,得出结论,因为值不同,对象不可能相等,所以调用Equals根本没有意义,然后继续前进。

要解决这个问题,要么从GetHashCode中删除Ret_USD因子,要么在Equals中引入它(无论对您的相等语义有什么意义)。

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

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

如果你想强制执行等号,你可以这样实现

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

像这样重写您的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是毫无意义的浪费,只是从哈希中排除。

看这里我知道的最好的通用答案,