---
title: "records ToString and inheritence"
description: "What is the output of that code snippet?\n```csharp\nConsole.Write(new Derived(\"Test\"));\n\npublic abstract record Base(string Key)\n{\n    public override string ToString() => Key;\n}\n\npublic sealed record DerivedRecord(string Key) : Base(Key);\n```\n\nProbably *\"Test\"*?"
updated: 2026-03-16
canonical: "https://steven-giesel.com/blogPost/9be39ff1-b7fc-4814-abd4-5ea41a07f8d7/records-tostring-and-inheritence"
---

# records ToString and inheritence

What is the output of that code snippet?
```csharp
Console.Write(new Derived("Test"));

public abstract record Base(string Key)
{
    public override string ToString() => Key;
}

public sealed record DerivedRecord(string Key) : Base(Key);
```

Probably *"Test"*?

## Lowered Code

If we have a look at the [following lowered code](https://sharplab.io/#v2:D4AQTADAsAUOCMtYGED2A7AzqgNgUwDoB1AJwEsAXPACnTwHcACAET3IDc8ATAJTwGNUJLtQBEAFTyYKogJSyA3EjgBmRgEMARtJLr+FRiQFCujAELrMNEPAiMA0ngCes2AG9YjL4xBrUnEnIuPB9bRnFUAGUKcnQAc2pZRgBeAD4HZyUYAF9lX0YrdXxTI0FhFjYyTl5jYWobO0cXRgAuc0saJsUgA=):

```csharp
[NullableContext(1)]
[Nullable(0)]
public abstract class Base : IEquatable<Base>
{
    [CompilerGenerated]
    private readonly string <Key>k__BackingField;

    [CompilerGenerated]
    protected virtual Type EqualityContract
    {
        [CompilerGenerated]
        get
        {
            return typeof(Base);
        }
    }

    public string Key
    {
        [CompilerGenerated]
        get
        {
            return <Key>k__BackingField;
        }
        [CompilerGenerated]
        init
        {
            <Key>k__BackingField = value;
        }
    }

    protected Base(string Key)
    {
        <Key>k__BackingField = Key;
        base..ctor();
    }

    public sealed override string ToString()
    {
        return Key;
    }
		
		// ....
}


[NullableContext(1)]
[Nullable(0)]
public sealed class DerivedRecord : Base, IEquatable<DerivedRecord>
{
    [CompilerGenerated]
    protected override Type EqualityContract
    {
        [CompilerGenerated]
        get
        {
            return typeof(DerivedRecord);
        }
    }

    public DerivedRecord(string Key)
        : base(Key)
    {
    }

    [CompilerGenerated]
    public override string ToString()
    {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.Append("DerivedRecord");
        stringBuilder.Append(" { ");
        if (PrintMembers(stringBuilder))
        {
            stringBuilder.Append(' ');
        }
        stringBuilder.Append('}');
        return stringBuilder.ToString();
    }

    [CompilerGenerated]
    protected override bool PrintMembers(StringBuilder builder)
    {
        return base.PrintMembers(builder);
    }
		
		/...
```

<sup>I left many parts out for simplicity</sup>

So `ToString` is "reimplemented" in the derived class, where we construct everything of the derived class and call `PrintMembers` of the base class. So if we call that snippet we get:

```no-class
DerivedRecord { Key = Test }
```

So not just *"Test"*. To fix that, C# 10 introduced that we are able to add `sealed` to methods that are offered by the `record` construct:

```diff
public abstract record Base(string Key)
{
-    public override string ToString() => Key;
+    public sealed override string ToString() => Key;
}

public sealed record DerivedRecord(string Key) : Base(Key);
```

And now we "just" have *"Test"* as output!