C# Equals中的SequenceEqual使GetHashCode中断
(1)我知道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) {
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
是对象的“肉”,那么忽略它是非常糟糕的。如果它只是许多部分中的一部分,那么即使忽略它也不会引起太多问题。