C# 9.0 records - ToString not inherited

5.2k views Asked by At

consider:

// the ratioale for Wrapper is that it has a Json serializer that  
// serialize through Field (not included in this example)
record Wrapper<T> where T : notnull {
  protected Wrapper(T field) => Field = field; 
  protected Wrapper(Wrapper<T> wrapper) => Field = wrapper.Field;
  protected readonly T Field;
  public override string ToString() => Field.ToString() ?? "";
}

record MyRec : Wrapper<string> {
    public MyRec(string s) : base(s) {}
}

public static class Program {
  public static void Main(string[] args) {
      var r = new MyRec("hello");
      Console.WriteLine(r.ToString());
  }
}

sharplab

seems like base ToString is not inherinted and the compiler still autogenerates derived Tostring

Why is that ? is there any good way around it ?

2

There are 2 answers

2
Billy On BEST ANSWER

There is a comment which has fixed this issue for me. Sean says if you add sealed to the method it stops the compiler from synthesizing the ToString method see below:

public sealed override string ToString() => Value;

Note that the ability to seal an override of ToString was introduced in C# 10.

7
D Stanley On

The record declaration supercedes the inherited ToString(). This is explained in the What's New description:

The compiler synthesizes two methods that support printed output: a ToString() override, and PrintMembers.

The fact that the base class (also a record) has a ToString override is not considered. Technically all types inherit ToString() from object, so the code generator for record types does not look at base types for an inherited override, otherwise a compiler-generated ToString would never be created.

(You could argue that a ToString in an inherited record type should be preserved, but that's not how the feature was designed).

But if the record type has an overridden ToString(), then the compiler does not generate one:

If a record type has a method that matches the signature of any synthesized method, the compiler doesn't synthesize that method.

You could define a seemingly redundant override:

public override string ToString() => base.ToString();

This should prevent the compiler from auto-generating a ToString override due to the record declaration.