C# Equals中的SequenceEqual使GetHashCode中断

C# Equals中的SequenceEqual使GetHashCode中断,c#,ienumerable,equals,gethashcode,C#,Ienumerable,Equals,Gethashcode,(1)我知道GetHashCode必须为两个相等的对象返回相同的数字 (2)我还知道SequenceEqual比较列表的每个值,并且Equals(list1,list2)仅当list1和list2是同一实例时才会返回true ,请考虑此代码: public List<ClassB> SampleList { get; set; } public string Str { get; set; } protected bool Equals(Uncorrectable other) {

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

(2)我还知道
SequenceEqual
比较
列表的每个值,并且
Equals(list1,list2)
仅当
list1
list2
是同一实例时才会返回true

,请考虑此代码:

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); 
    }
}
hash1
hash2
不相等

所以在我的
ClassA
(1)不受尊重

最佳解决方案是什么:

  • ClassA.Equals
    方法更改为使用
    Equals(this.SampleList,other.SampleList)
    而不是
    Enumerable.SequenceEqual(this.SampleList,other.SampleList)
    并更改我的所有测试
  • 创建另一个
    IEnumerable
    实现,其中
    Equals
    覆盖类似于
    SequenceEqual
  • 修改
    ClassA.GetHashCode
    以调用所有列表项上的
    GetHashCode
  • 无所事事
  • 再来一个

  • 不要将
    GetHashCode
    建立在
    SampleList
    上:您不需要使用
    GetHashCode()
    中的所有字段/属性

    例如:

    unchecked 
    { 
        return 
            (this.Str != null ? this.Str.GetHashCode() : 0); 
    }
    
    unchecked 
    { 
        return 
            ((this.SampleList != null ? this.SampleList.Count.GetHashCode() : 0) * 397) ^
            (this.Str != null ? this.Str.GetHashCode() : 0); 
    }
    
    或者最好只使用
    样本列表的一些信息
    。。。
    计数
    例如:

    unchecked 
    { 
        return 
            (this.Str != null ? this.Str.GetHashCode() : 0); 
    }
    
    unchecked 
    { 
        return 
            ((this.SampleList != null ? this.SampleList.Count.GetHashCode() : 0) * 397) ^
            (this.Str != null ? this.Str.GetHashCode() : 0); 
    }
    
    如果确实需要,您可以在
    SampleList
    的元素上计算
    GetHashCode()

    现在,对于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
    循环)

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

    例如:

    unchecked 
    { 
        return 
            (this.Str != null ? this.Str.GetHashCode() : 0); 
    }
    
    unchecked 
    { 
        return 
            ((this.SampleList != null ? this.SampleList.Count.GetHashCode() : 0) * 397) ^
            (this.Str != null ? this.Str.GetHashCode() : 0); 
    }
    
    或者最好只使用
    样本列表的一些信息
    。。。
    计数
    例如:

    unchecked 
    { 
        return 
            (this.Str != null ? this.Str.GetHashCode() : 0); 
    }
    
    unchecked 
    { 
        return 
            ((this.SampleList != null ? this.SampleList.Count.GetHashCode() : 0) * 397) ^
            (this.Str != null ? this.Str.GetHashCode() : 0); 
    }
    
    如果确实需要,您可以在
    SampleList
    的元素上计算
    GetHashCode()

    现在,对于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
    循环)

    现在很明显了(在我脑子里的某个地方,我认为(1)的倒数一定是真的)。。如果ClassA不是设计在一个大列表中(我将只有10个不同的ClassA实例),并且我真的不需要性能,那么我们是否同意我可以使用第二个解决方案(使用长度或其他字段)?@ZwoRmi是的。请注意,计算大型
    样本列表的哈希值通常可能非常长。。。例如,你甚至可以“作弊”,只在散列中包含一个固定的子集
    (this.SampleList.Count*397)^this.SampleList.Take(5).Aggregate
    如果完全忽略列表,冲突率可能非常高,导致使用哈希代码的任何内容的性能都非常差。@Servy这一切都取决于存在的其他字段的数量。显然,如果
    SampleList
    是对象的“肉”,那么忽略它是非常糟糕的。如果它只是许多片段中的一个,那么即使忽略它也不会引起太多问题。现在很明显(在我大脑中的某个地方,我认为(1)的倒数一定是真的)。。如果ClassA不是设计在一个大列表中(我将只有10个不同的ClassA实例),并且我真的不需要性能,那么我们是否同意我可以使用第二个解决方案(使用长度或其他字段)?@ZwoRmi是的。请注意,计算大型
    样本列表的哈希值通常可能非常长。。。例如,你甚至可以“作弊”,只在散列中包含一个固定的子集
    (this.SampleList.Count*397)^this.SampleList.Take(5).Aggregate
    如果完全忽略列表,冲突率可能非常高,导致使用哈希代码的任何内容的性能都非常差。@Servy这一切都取决于存在的其他字段的数量。显然,如果
    SampleList
    是对象的“肉”,那么忽略它是非常糟糕的。如果它只是许多部分中的一部分,那么即使忽略它也不会引起太多问题。