C# Override Records ToString
Karen Payne

Karen Payne @karenpayneoregon

About: Microsoft MVP, Microsoft TechNet author, Code magazine author, developer advocate. Have a passion for driving race cars.

Location:
Oregon, USA
Joined:
Jan 1, 2023

C# Override Records ToString

Publish Date: Jun 9
3 1

Introduction

Learn how to control formatting records by overriding ToString method along with a virtual method PrintMembers. By controlling formatting, a developer can format for outputting to a frontend, along with hiding one or more properties or redacting data.

The record used where based on a condition will hide BirthDate, mask SSN, and fully redact UserName and Password. It will also present PhoneNumbers as a comma-delimited list.

public partial record Person(
    string FirstName, 
    string LastName, 
    DateOnly BirthDate, 
    string SSN,
    string UserName,
    string Password,
    string[] PhoneNumbers) : ITaxpayer { }
Enter fullscreen mode Exit fullscreen mode

Shows full and redacted output

Source code

Controlling output

For demonstration, if the project runs from Microsoft Visual Studio, show all property values, and when not executed from Microsoft Visual Studio, redaction is done. When the project is moved to production, either leave the code as is or set RevealSensitiveInformation to false. A json is not used as a user could change the setting. An alternative is to use an environment variable.

Demonstration data

public class MockedData
{
    public static List<Person> SamplePeople() =>
    [
        new("Jane", "Smith", new DateOnly(1990, 5, 22), 
            "987-65-4321","jsmith","pass1", []),
        new("Karen", "Payne", new DateOnly(1985, 1, 15), 
            "123-45-6789", "kpayne", "pass2",["555-1234", "555-5678","154-3557"]),
        new("Michael", "Johnson", new DateOnly(1978, 3, 10), 
            "987-65-4321", "mjohnson", "pass3",["555-1111", "555-2222"]),
        new("Emily", "Davis", new DateOnly(1995, 7, 30),
            "456-78-9012",  "edavis", "pass4",["555-3333"]),
        new("David", "Wilson", new DateOnly(1982, 11, 5), 
            "321-54-9876", "dwilson", "pass5",["555-4444", "555-5555"]),
    ];
}
Enter fullscreen mode Exit fullscreen mode

Code to override the default for the above record

The code is placed in a partial class which is optional.

public partial record Person
{
    public override string ToString()
    {
        var builder = new StringBuilder();
        var person = this;


        if (!Appsettings.Instance.RevealSensitiveInformation)
        {

            person = person with
            {
                BirthDate = default,
                UserName = "redacted",
                Password = "redacted"
            };
        }

        person.PrintMembers(builder);

        return builder.ToString();
    }

    protected virtual bool PrintMembers(StringBuilder builder)
    {
        if (Appsettings.Instance.RevealSensitiveInformation)
        {
            builder.Append($"{FirstName,-10}{LastName,-10}{SSN,-13}{BirthDate,-14:MM/dd/yyyy}{UserName,-10}{Password,-12}");
        }
        else
        {
            builder.Append($"{FirstName,-10}{LastName,-10}{SSN.MaskSsn(),-13}{BirthDate,-14:MM/dd/yyyy}{UserName,-10}{Password,-12}");
        }


        if (!(PhoneNumbers?.Length > 0))
        {
            builder.Append("None");
            return true;
        }

        builder.Append(string.Join(", ", PhoneNumbers));
        builder.Append("");

        return true;

    }
}
Enter fullscreen mode Exit fullscreen mode

ToString override

  • Set Person to the current instance.
  • If reaction, create and new instance of Person redacting BirthDate, UserName and Password using nondestructive mutation.
  • Pass the StringBuilder to PrintMembers which redacts depending on the value of Appsettings.Instance.RevealSensitiveInformation.

For masking SSN, a language extension MaskSsn is utilized.

Usage

internal partial class Program
{
    static void Main(string[] args)
    {
        var people = MockedData.SamplePeople();
        AnsiConsole.MarkupLine("[cyan]First     Last      SSN          Birthdate     " +
                               "UserName  Password    Phone numbers[/]");
        foreach (var person in people)
        {
            AnsiConsole.MarkupLine(person.Colorize());
        }

        SpectreConsoleHelpers.ExitPrompt();
    }
}
Enter fullscreen mode Exit fullscreen mode

Colorize is a language extension method to use Spectre.Console colors for output to the console.

Overriding ToString only

In some cases, using PrintMembers is not necessary, as in the following case, for the conditional output of the Line2 property.

public record Address(string Line1, string? Line2, string City, string Country, string PostCode)
{
    public override string ToString() =>
        $"{Line1}, {(Line2 is not null ? Line2 + ", " : string.Empty)}{City}, {Country}, {PostCode}";
}
Enter fullscreen mode Exit fullscreen mode

results

private static void DisplayAddressExample()
{
    var addresses = new List<Address>
    {
        new("123 Main St", null, "Portland", "USA", "97201"),
        new("456 Maple Ave", "Apt 4B", "Eugene", "USA", "97401"),
        new("789 Oak Dr", "", "Salem", "USA", "97301")
    };

    foreach (var address in addresses)
    {
        Console.WriteLine(address);
    }
}
Enter fullscreen mode Exit fullscreen mode

Summary

With the information provided, a developer can format and redact data for record types.

Comments 1 total

  • Jamie H
    Jamie HJun 11, 2025

    Nice posting! Looking forward to talking to you.

Add comment