1

Is there any possibility to tell the C# compiler that a deeply immutable record type (C# 9+) can shortcut its Equals check by returning true immediately, when the checked objects have the same reference?

With "deeply immutable record" I mean that all properties are immutable in any depth:

public record MyImmutableRecord {
   public int MyProp { get; init; }
   public IReadOnlyList<AlsoImmutable> MyList { get; init; }
}

The compiler automatically creates an Equals method which checks all properties. From ILSpy:

public virtual bool Equals(MyImmutableRecord? other)
{
   return (object)other != null
     && EqualityContract == other!.EqualityContract
     && EqualityComparer<int>.Default.Equals(MyProp, other!.MyProp)
     && ...
}

This could be speeded up enormously by checking for the reference first. When equal, we know for sure that the content is equal too. If not, we have to continue by checking the properties :

public virtual bool Equals(MyImmutableRecord? other) {
   if (object.ReferenceEquals(this, other)
     return true;
   return ... // Like above
}

Any chance to tell the compiler that we have such a truely immutable object and using this trick? Or does anybody know if this is planned for the future? Otherwise I guess I'll have to write my own Equals implementation for each immutable record?

Andi
  • 3,234
  • 4
  • 32
  • 37
  • 4
    I'm not sure what you think "truly immutable" brings to this party. If you have two references to the same object, why does mutability effect equality? E.g. if it's the same object, then any references it contains will only exist once and so will of course be to the same referenced object. – Damien_The_Unbeliever Feb 10 '21 at 11:37
  • In my example, if I used `List` instead of `IReadOnlyList`, I could change the contents of the list from outside. The reference of the main object would remain unchanged, but its content changed. That is why "true" or "deep" immutability is required for my proposal. – Andi Feb 10 '21 at 11:40
  • 1
    But... If it's still the same reference then... well, they are the same. Your proposed modification to Equals is checking the record for reference equality, nothing more. Mutability still plays no part. Unless you mean to ask a different question then the one you have actually asked, that is – pinkfloydx33 Feb 10 '21 at 11:52
  • OK, now I think I understand what you mean... But doen't this mean that two objects are _always_ equal, independent of their mutability, when their reference is equal? If so, why does the auto-generated `Equals` method do not contain this check? – Andi Feb 10 '21 at 12:13
  • 2
    @Andi that's a better question for the csharplang github – pinkfloydx33 Feb 10 '21 at 12:14
  • 1
    As an aside, note that your record may not be truly immutable. Under some circumstances, a devious developer could cast the list to the concrete List<> type and modify the collection – pinkfloydx33 Feb 10 '21 at 12:16
  • That's true. I used `IReadOnlyList` to make the example a little bit simpler. In reality, I use my own "truely immutable/persistent" collection type. Still missing a good [persistent collection](https://en.wikipedia.org/wiki/Persistent_data_structure) library for C# :-) (Another question...) – Andi Feb 10 '21 at 12:30
  • What's wrong with `is` – Charlieface Feb 10 '21 at 16:12

1 Answers1

4

The answer is very easy (thanks for the comments). Of course, two objects are always equal when their reference is is equal... Independent of their mutability. Probably I just came across this idea because immutable data structures encourage developers to reuse and share objects all over the code, much more than mutable classes do.

The question should have been:

Why does the auto-generated Equals method do not contain a check for reference equality first, before checking the members in detail?

I ask this in the csharplang project: https://github.com/dotnet/csharplang/discussions/4411

EDIT 2021-02-11: This is now on the roadmap for Visual Studio 16.10: https://github.com/dotnet/roslyn/issues/51136

Andi
  • 3,234
  • 4
  • 32
  • 37
  • Also discussed in the c# [design meeting](https://github.com/dotnet/csharplang/blob/master/meetings/2021/LDM-2021-02-10.md#follow-up-on-record-equality), making it official part of the spec – pinkfloydx33 Feb 13 '21 at 09:22
  • Now, the generated code looks like return `(object)this == other || ...`. I do not understand why they chose the cast and equality operator instead of ReferenceEquals(), though... Edit: Just found a Q&A about that... https://stackoverflow.com/q/735554/2157640 – Palec Dec 09 '22 at 09:51