C# abstract record with abstract Equals method
Asked Answered
K

1

6

Why are we not allowed to do the following for a record

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
  }
}

while it is totally acceptable for classes?

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

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

By the way, I am doing this implementation to enforce the Equals check to cascade to its derived classes.

Edit: just to give context on why am I interested on this is because I am currently creating an library/autogenerator for IEquatable.

Edit/Info 2: Based on the comments, I have done some tests. since the abstract Equals method of record can't be overridden, I tried leaving it as is.

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; }
}

The result I get an error of System.BadImageFormatException: Bad IL format.

All in all, abstract Equals method on records is not just an unnecessary implementation but is also a bad one.

Kym answered 3/5, 2021 at 8:58 Comment(0)
O
3

This is because compiler already implements equality methods for records. Check Value equality of records:

To implement value equality, the compiler synthesizes the following methods:

  • An override of Object.Equals(Object).

  • This method is used as the basis for the Object.Equals(Object, Object) static method when both parameters are non-null.

  • A virtual Equals method whose parameter is the record type. This method implements IEquatable.

  • An override of Object.GetHashCode().

  • Overrides of operators == and !=.

It means that there's not need to enforce the implementation of Equals method in the base abstract record.

If you still want to have custom Equals method implementation and derive from some base record you could do so by declaring virtual Equals method with the derived type as an argument:

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();
    }
}
Ocana answered 3/5, 2021 at 9:15 Comment(4)
The page you linked to also says "If a record type has a method that matches the signature of any synthesized method, the compiler doesn't synthesize that method", which means that, in this particular case, the compiler should not synthesize such a method.Rosmunda
In case of inheritance the public bool Equals(AA other) is generated in base record, which causes an error. It would be valid to create public virtual bool Equals(BB other) in the derived record though.Ocana
All in all, not only is public abstract bool Equals(AA other); an unnecessary implementation for records, but also a bad one.Kym
@MKtsisD I couldn't prove in my tests that adding abstract Equals to abstract record changes behavior in any scenario that I tests. It can be misleading, yes, but not really harmful. I would also advice against a base record for records as when comparing records if two different derived types lead to comparison of one record and a null due to the underlying casting.Ocana

© 2022 - 2024 — McMap. All rights reserved.