如何通过继承正确自定义 c# 记录相等性
How to properly customize c# record equality with inheritance
鉴于以下情况,
public record Foo(int Id)
{
public virtual bool Equals(Foo? foo)
{
Console.WriteLine($"foo {(foo is null ? "IS" : "is NOT")} null");
return foo is not null && Id == foo.Id;
}
public override int GetHashCode() => Id.GetHashCode();
}
public record FooSummary(int Id, string Summary) : Foo(Id);
public record FooDetail(int Id, string Detail) : Foo(Id);
var summary = new FooSummary(1, "Summary");
var detail = new FooDetail(1, "Detail");
Console.WriteLine(detail == summary);
// Output:
// foo IS null
// false
是否可以通过 detail == summary
为 true
的方式自定义记录相等性?
在 class 中我可以覆盖 Equals(object obj)
,但在导致编译错误的记录中 (CS0111)
编辑
我接受了@StriplingWarrior 的回答,因为它在技术上回答了我的问题,但正如他和其他人所解释的那样:这是一个坏主意。这绝对是 XY 问题的一个例子,为了我自己的利益,我想 太 聪明一点。
C# 9 Documentation 看起来很清楚:
Two variables of a record type are equal if the record type definitions are identical, and if for every field, the values in both records are equal.
我的理解是它没有为 record
类型自定义相等性留下空间。
从技术上讲,您可以覆盖 ==
运算符。
public record Foo(int Id)
{
public virtual bool Equals(Foo? foo)
{
return foo is not null && Id == foo.Id;
}
public override int GetHashCode() => Id.GetHashCode();
}
public record FooSummary(int Id, string Summary) : Foo(Id)
{
public bool Equals(FooDetail? other)
{
return base.Equals((Foo?)other);
}
public override int GetHashCode() => base.GetHashCode();
}
public record FooDetail(int Id, string Detail) : Foo(Id)
{
public bool Equals(FooSummary? other)
{
return base.Equals((Foo?)other);
}
public static bool operator ==(FooDetail? left, FooSummary? right)
=> (object?)left == right || (left?.Equals(right) ?? false);
public static bool operator !=(FooDetail? left, FooSummary? right)
=> !(left == right);
public static bool operator ==(FooSummary? left, FooDetail? right)
=> (object?)left == right || (left?.Equals(right) ?? false);
public static bool operator !=(FooSummary? left, FooDetail? right)
=> !(left == right);
public override int GetHashCode() => base.GetHashCode();
}
这并不意味着这是个好主意。当您的类型显式转换为您尝试比较的特定类型时,您会遇到意外行为。
根据我的经验,当人们试图覆盖相等运算符时,这通常是他们想要完成的目标的错误工具。
鉴于以下情况,
public record Foo(int Id)
{
public virtual bool Equals(Foo? foo)
{
Console.WriteLine($"foo {(foo is null ? "IS" : "is NOT")} null");
return foo is not null && Id == foo.Id;
}
public override int GetHashCode() => Id.GetHashCode();
}
public record FooSummary(int Id, string Summary) : Foo(Id);
public record FooDetail(int Id, string Detail) : Foo(Id);
var summary = new FooSummary(1, "Summary");
var detail = new FooDetail(1, "Detail");
Console.WriteLine(detail == summary);
// Output:
// foo IS null
// false
是否可以通过 detail == summary
为 true
的方式自定义记录相等性?
在 class 中我可以覆盖 Equals(object obj)
,但在导致编译错误的记录中 (CS0111)
编辑
我接受了@StriplingWarrior 的回答,因为它在技术上回答了我的问题,但正如他和其他人所解释的那样:这是一个坏主意。这绝对是 XY 问题的一个例子,为了我自己的利益,我想 太 聪明一点。
C# 9 Documentation 看起来很清楚:
Two variables of a record type are equal if the record type definitions are identical, and if for every field, the values in both records are equal.
我的理解是它没有为 record
类型自定义相等性留下空间。
从技术上讲,您可以覆盖 ==
运算符。
public record Foo(int Id)
{
public virtual bool Equals(Foo? foo)
{
return foo is not null && Id == foo.Id;
}
public override int GetHashCode() => Id.GetHashCode();
}
public record FooSummary(int Id, string Summary) : Foo(Id)
{
public bool Equals(FooDetail? other)
{
return base.Equals((Foo?)other);
}
public override int GetHashCode() => base.GetHashCode();
}
public record FooDetail(int Id, string Detail) : Foo(Id)
{
public bool Equals(FooSummary? other)
{
return base.Equals((Foo?)other);
}
public static bool operator ==(FooDetail? left, FooSummary? right)
=> (object?)left == right || (left?.Equals(right) ?? false);
public static bool operator !=(FooDetail? left, FooSummary? right)
=> !(left == right);
public static bool operator ==(FooSummary? left, FooDetail? right)
=> (object?)left == right || (left?.Equals(right) ?? false);
public static bool operator !=(FooSummary? left, FooDetail? right)
=> !(left == right);
public override int GetHashCode() => base.GetHashCode();
}
这并不意味着这是个好主意。当您的类型显式转换为您尝试比较的特定类型时,您会遇到意外行为。
根据我的经验,当人们试图覆盖相等运算符时,这通常是他们想要完成的目标的错误工具。