C#带有抽象Equals方法的抽象记录

4
为什么我们不被允许为记录执行以下操作?
abstract record AA
{
  public abstract bool Equals(AA other);
}

record BB:AA
{
  public override bool Equals(AA other)// error as it is already implemented
  {
    //do some thing
  }
}

虽然对于类而言,这是完全可以接受的?

abstract class AA
{
   public abstract bool Equals(AA other);
}

class BB:AA
{
  public override bool Equals(AA other)
  {
    //do some thing
  }
}

顺便说一下,我正在进行这个实现以强制执行Equals检查至其派生类。

编辑:为了说明我为什么对此感兴趣,因为我目前正在创建一个IEquatable的库/自动生成器

编辑/信息2:根据评论,我进行了一些测试。由于记录的抽象Equals方法无法被覆盖,所以我尝试将其保留为原样。

public abstract record AA
{
    public int Prop1 { get; set; }
    public string? Prop2 { get; set; }
    public string? Prop5 { get; set; }
    public abstract bool Equals(AA? other);
}

public record BB : AA
{
    public string? Prop3 { get; set; }
}

我得到了一个错误的结果:System.BadImageFormatException: 糟糕的IL格式。

总之,对于记录而言,抽象Equals方法不仅是不必要的实现,而且是一种不好的实现。

1个回答

3
这是因为编译器已经为记录实现了相等方法。请查看记录的值相等性:值相等性
为了实现值相等性,编译器合成了以下方法:
- 一个Object.Equals(Object)的重载方法。 - 当两个参数都不为null时,该方法用作Object.Equals(Object, Object)静态方法的基础。 - 一个虚拟Equals方法,其参数是记录类型。此方法实现了IEquatable。 - 一个Object.GetHashCode()的重载方法。 - 操作符==和!=的重载方法。
这意味着在基本抽象记录中没有必要强制实现Equals方法。
如果您仍然想要自定义的Equals方法实现,并从某个基本记录派生,可以通过声明带有派生类型作为参数的虚拟Equals方法来实现。
abstract record AA
{
    // Redundant in case of records and can be omitted.
    public abstract bool Equals(AA other);
}

record BB : AA
{
    public virtual bool Equals(BB other)
    {
        throw new NotImplementedException();
    }
}

1
你链接的页面还说:“如果记录类型具有与任何合成方法签名匹配的方法,则编译器不会合成该方法”,这意味着在这种特殊情况下,编译器不应合成这样的方法。 - Heinzi
在继承的情况下,基础记录中会生成public bool Equals(AA other),这会导致错误。但在派生记录中创建public virtual bool Equals(BB other)是有效的。 - Andrii Litvinov
总的来说,public abstract bool Equals(AA other); 不仅是记录类型中不必要的实现,而且还是一个糟糕的实现。 - Marlon Dumal-is
@MKtsisD 我在测试中无法证明将抽象的 Equals 添加到抽象记录会改变任何我测试过的情况下的行为。这可能会产生误导,但并不真正有害。我还建议不要为记录使用基础记录,因为当比较记录时,如果两个不同的派生类型导致由于底层转换而比较一个记录和 null - Andrii Litvinov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接