- Notifications
You must be signed in to change notification settings - Fork1.1k
Description
Records with sealed base ToString override
- Proposed
- Prototype: Done
- Implementation: Done
- Specification: Not Started
Summary
This proposal would lift the restriction on declaring sealedToString
overrides in non-sealed records and lift the restriction on a record inheriting from a base type that has a sealedToString
override. The semantics ofsealed override
are clear, they are appropriate for closed hierarchies whereToString
is controlled by the base, and the current errors seem to deny its otherwise consistent functioning out of an overabundance of caution.
Motivation
There's currently no way to specify aToString
implementation once for all derived records. Because of this, the need to overrideToString
can have a jarring result. Consider this hand-rolled discriminated union based on records:
publicabstractrecordSomeDiscriminatedUnion(stringName){publicsealedrecordFirstKind(stringName,intA):SomeDiscriminatedUnion(Name);publicsealedrecordSecondKind(stringName,intB):SomeDiscriminatedUnion(Name);publicsealedrecordThirdKind(stringName,boolC):SomeDiscriminatedUnion(Name);publicsealedrecordFourthKind(stringName):SomeDiscriminatedUnion(Name);}
OverridingToString
has no effect on derived records unless each derived record is given a manual override that returnsbase.ToString()
. Sealing the override would perfectly describe the intended result, but this is currently blocked by the compiler with errors CS0239 and CS8870.
The simplest option currently available is much less pleasing. One factor is its repetition. Another factor is that the base record no longer contains a readable list of single-line members.
publicabstractrecordSomeDiscriminatedUnion(stringName){publicsealedrecordFirstKind(stringName,intA):SomeDiscriminatedUnion(Name){publicoverridestringToString()=>Name;}publicsealedrecordSecondKind(stringName,intB):SomeDiscriminatedUnion(Name){publicoverridestringToString()=>Name;}publicsealedrecordThirdKind(stringName,boolC):SomeDiscriminatedUnion(Name){publicoverridestringToString()=>Name;}publicsealedrecordFourthKind(stringName):SomeDiscriminatedUnion(Name){publicoverridestringToString()=>Name;}}
Detailed design
Non-sealed records would support sealed overrides ofToString
the same way classes do rather than producing error "CS8870 'BaseRecord.ToString()' cannot be sealed because containing record is not sealed."
Derived records would support inheriting when the baseToString
is sealed by skipping the synthesizedToString
override rather than producing error "CS0239 'DerivedRecord.ToString()': cannot override inherited member 'BaseRecord.ToString()' because it is sealed."
Drawbacks
Alternatives
A community source generator could be developed that would look for an attribute on the base record or the base record'sToString
override and addpublic override string ToString() => base.ToString();
to each derived record that doesn't already declare aToString
member. This feels like a workaround at best. The semantics ofsealed override
are clear, they are appropriate for closed hierarchies whereToString
is controlled by the base, and the current errors seem to deny its otherwise consistent functioning out of an overabundance of caution.