Equals 中的 SequenceEqual 使 GetHashCode 损坏
SequenceEqual in Equals makes GetHashCode broken
(1) 我知道如果两个对象相等,GetHashCode
必须 return 相同的数字。
(2)我也知道SequenceEqual
比较一个List
的每个值,Equals(list1, list2)
会return仅当 list1
和 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);
}
}
我真的需要这种行为((2) 使用 SequenceEqual
) Equals
,主要用于单元测试:制作此代码 Assert.AreEqual(classA1, classA2)
工作。
但我的某些代码可能已损坏,因为在这种情况下
int hash1 = new List<ClassB>().GetHashCode();
int hash2 = new List<ClassB>().GetHashCode();
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()
中的所有 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
循环)
(1) 我知道如果两个对象相等,GetHashCode
必须 return 相同的数字。
(2)我也知道SequenceEqual
比较一个List
的每个值,Equals(list1, list2)
会return仅当 list1
和 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);
}
}
我真的需要这种行为((2) 使用 SequenceEqual
) Equals
,主要用于单元测试:制作此代码 Assert.AreEqual(classA1, classA2)
工作。
但我的某些代码可能已损坏,因为在这种情况下
int hash1 = new List<ClassB>().GetHashCode();
int hash2 = new List<ClassB>().GetHashCode();
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()
中的所有 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
循环)