如何通过继承正确自定义 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 == summarytrue 的方式自定义记录相等性?

在 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();
}

这并不意味着这是个好主意。当您的类型显式转换为您尝试比较的特定类型时,您会遇到意外行为。

根据我的经验,当人们试图覆盖相等运算符时,这通常是他们想要完成的目标的错误工具。