Equals中的SequenceEqual使GetHashCode损坏

时间:2016-02-19 14:59:38

标签: c# ienumerable equals gethashcode

(1)我知道GetHashCode必须为两个对象返回相同的数字(如果它们相等)。

(2)我也知道SequenceEqual比较List的每个值,而Equals(list1, list2)仅在list1时才会返回true list2是同一个实例。

所以,请考虑以下代码:

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); 
    }
}

对于SequenceEqual,我确实需要这种行为((2)使用Equals),主要用于单元测试:使此代码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. 的实施
  4. 修改ClassA.GetHashCode以在所有列表项上调用GetHashCode
  5. 什么都不做
  6. 另一个?

1 个答案:

答案 0 :(得分:1)

只是不要将GetHashCode作为SampleList的基础:您不需要使用GetHashCode()中的所有字段/属性。

例如:

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); 
}

如果您真的想要,可以计算GetHashCode()元素上的SampleList

现在,对于C#混淆代码锦标赛,2016版:

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周期)