Home > Software engineering >  How to properly customize c# record equality with inheritance
How to properly customize c# record equality with inheritance

Time:05-25

Given the following,

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

Is it possible to customize record equality is such a way that detail == summary is true?

In a class I could override Equals(object obj), but in a record that results in a compilation error (CS0111)

CodePudding user response:

Technically, you can, by overriding the == operators.

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

That doesn't mean it's a good idea. You'll get unexpected behaviors when your types are explicitly cast as the specific types you're trying to compare.

In my experience, when people are tempted to override equality operators, that's usually the wrong tool for what they're trying to accomplish.

CodePudding user response:

C# 9 Documentation seems pretty clear:

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.

My read of this is that it leaves no room to customize equality for a record type.

  • Related