记录类型:重写 EqualityContract 会中断 equality/hashcode 匹配
Record types: overriding EqualityContract breaks the equality/hashcode match
新引入的记录类型允许覆盖 EqualityContract
类型,这使得可以创建一种情况,当两个对象相等但具有不同的哈希码时,反对 guidelines 覆盖 GetHashCode
:
If you override the GetHashCode method, you should also override Equals, and vice versa. If your overridden Equals method returns true when two objects are tested for equality, your overridden GetHashCode method must return the same value for the two objects.
public record Base(string Foo);
public record Child(string Foo, string Bar) : Base(Foo)
{
protected override Type EqualityContract => typeof(Base);
}
var b = new Base("Foo");
var c = new Child("Foo", "Bar");
Console.WriteLine(b == c); // True
Console.WriteLine(b.GetHashCode() == c.GetHashCode()); // False
从 Child
中删除额外的 属性 会使 GetHashCode
和 Equals
“匹配”。
显然这可以通过覆盖 GetHashCode
来解决,但我想知道为什么覆盖 EqualityContract
不会自动导致 GetHashCode
return 值“匹配”Equals
实施?或者除了手动 GetHashCode
覆盖之外,还有其他方法可以处理这个问题。
如您在 equality members part of records proposal、
中所见
GetHashCode()
returns an int
result of a deterministic function
combining the following values:
For each instance field fieldN
in the record type that is not inherited, the value of
System.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN)
where TN is the field type, and
If there is a base record type, the value of base.GetHashCode(); otherwise the value of
System.Collections.Generic.EqualityComparer<System.Type>.Default.GetHashCode(EqualityContract)
.
因此,在您的示例中,GetHashCode
对于 Base
将是
public override int GetHashCode()
{
return EqualityComparer<Type>.Default.GetHashCode(EqualityContract) + EqualityComparer<string>.Default.GetHashCode(Foo);
}
而对于 Child
public override int GetHashCode()
{
return base.GetHashCode() + EqualityComparer<string>.Default.GetHashCode(Bar);
}
对于继承的记录,EqualityContract
不用于哈希码计算。如果删除了额外的 属性 Bar
,则使用 Base
中的值,您将获得哈希值相等。因此,需要覆盖 GetHashCode
。
使用 sharplab.io
也可以观察到这种行为
新引入的记录类型允许覆盖 EqualityContract
类型,这使得可以创建一种情况,当两个对象相等但具有不同的哈希码时,反对 guidelines 覆盖 GetHashCode
:
If you override the GetHashCode method, you should also override Equals, and vice versa. If your overridden Equals method returns true when two objects are tested for equality, your overridden GetHashCode method must return the same value for the two objects.
public record Base(string Foo);
public record Child(string Foo, string Bar) : Base(Foo)
{
protected override Type EqualityContract => typeof(Base);
}
var b = new Base("Foo");
var c = new Child("Foo", "Bar");
Console.WriteLine(b == c); // True
Console.WriteLine(b.GetHashCode() == c.GetHashCode()); // False
从 Child
中删除额外的 属性 会使 GetHashCode
和 Equals
“匹配”。
显然这可以通过覆盖 GetHashCode
来解决,但我想知道为什么覆盖 EqualityContract
不会自动导致 GetHashCode
return 值“匹配”Equals
实施?或者除了手动 GetHashCode
覆盖之外,还有其他方法可以处理这个问题。
如您在 equality members part of records proposal、
中所见
GetHashCode()
returns anint
result of a deterministic function combining the following values:
For each instance field
fieldN
in the record type that is not inherited, the value ofSystem.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN)
where TN is the field type, andIf there is a base record type, the value of base.GetHashCode(); otherwise the value of
System.Collections.Generic.EqualityComparer<System.Type>.Default.GetHashCode(EqualityContract)
.
因此,在您的示例中,GetHashCode
对于 Base
将是
public override int GetHashCode()
{
return EqualityComparer<Type>.Default.GetHashCode(EqualityContract) + EqualityComparer<string>.Default.GetHashCode(Foo);
}
而对于 Child
public override int GetHashCode()
{
return base.GetHashCode() + EqualityComparer<string>.Default.GetHashCode(Bar);
}
对于继承的记录,EqualityContract
不用于哈希码计算。如果删除了额外的 属性 Bar
,则使用 Base
中的值,您将获得哈希值相等。因此,需要覆盖 GetHashCode
。
使用 sharplab.io
也可以观察到这种行为