Equals 中的 SequenceEqual 使 GetHashCode 损坏

SequenceEqual in Equals makes GetHashCode broken

(1) 我知道如果两个对象相等,GetHashCode 必须 return 相同的数字。

(2)我也知道SequenceEqual比较一个List的每个值,Equals(list1, list2)会return仅当 list1list2 是同一实例时才为真。

因此,请考虑以下代码:

public List<ClassB> SampleList { get; set; }
public string Str { get; set; }
protected bool Equals(Uncorrectable other)
{
    return Enumerable.SequenceEqual(this.SampleList, other.SampleList) && string.Equals(this.Str, other.Str);
}

public override bool Equals(object obj)
{
    if (ReferenceEquals(null, obj)) { return false; }
    if (ReferenceEquals(this, obj)) { return true; }
    if (obj.GetType() != this.GetType()) { return false; }
    return this.Equals((ClassA) obj);
}

public override int GetHashCode()
{
    unchecked 
    { 
        return 
            ((this.SampleList != null ? this.SampleList.GetHashCode() : 0)*397) ^
            (this.Str != null ? this.Str.GetHashCode() : 0); 
    }
}

我真的需要这种行为((2) 使用 SequenceEqualEquals,主要用于单元测试:制作此代码 Assert.AreEqual(classA1, classA2) 工作。

但我的某些代码可能已损坏,因为在这种情况下

int hash1 = new List<ClassB>().GetHashCode(); 
int hash2 = new List<ClassB>().GetHashCode(); 

hash1hash2 不相等。

所以在我的ClassA中,(1)不被尊重。

什么是最佳解决方案:

  1. 更改 ClassA.Equals 方法以使用 Equals(this.SampleList, other.SampleList) 而不是 Enumerable.SequenceEqual(this.SampleList, other.SampleList) 并更改我所有的测试
  2. 创建另一个 IEnumerable 实现,其中 Equals 覆盖类似于 SequenceEqual
  3. 修改 ClassA.GetHashCode 以在所有列表项上调用 GetHashCode
  4. 什么都不做
  5. 另一个?

不要将 GetHashCode 基于 SampleList:您不需要使用 GetHashCode() 中的所有 fields/properties。

例如:

unchecked 
{ 
    return 
        (this.Str != null ? this.Str.GetHashCode() : 0); 
}

甚至更好地只使用 SampleList 的一些信息... Count 例如:

unchecked 
{ 
    return 
        ((this.SampleList != null ? this.SampleList.Count.GetHashCode() : 0) * 397) ^
        (this.Str != null ? this.Str.GetHashCode() : 0); 
}

如果确实需要,可以在 SampleList 元素 上计算 GetHashCode()

现在,2016 年 C# 混淆代码锦标赛:

unchecked
{
    return
        (397 * (this.SampleList != null ? 
            this.SampleList.Aggregate(0, (old, curr) => 
                (old * 397) ^ (curr != null ? curr.GetHashCode() : 0)) : 
            0)
        ) ^
        (this.Str != null ? this.Str.GetHashCode() : 0);
} 

(请不要那样写...使用foreach循环)