Records: the () way and the {} way

1 min read
On trails:CSTYPESstep 4/6

A record is declared either with its data in parentheses (positional) or in a body (property form). Both have value equality, with and ToString, as in recordsRecordsA class for immutable data, with value-based equality generated for you.Opens in a new tab.

Positional

Each parameter becomes an init-only property. The compiler also writes a constructor that takes them in order, and a Deconstruct method.

C#
public record Person(string FirstName, string LastName); var p = new Person("Ada", "Lovelace");var (first, last) = p; // Deconstruct, written for you

Property form

The properties are written out and set with an object initializer. No constructor or Deconstruct is generated. required (C# 11) makes the compiler insist on a value.

C#
public record Person{    public required string FirstName { get; init; }    public required string LastName { get; init; }} var p = new Person { FirstName = "Ada", LastName = "Lovelace" };

The two can be combined: parameters for the essential data, a body for the rest.

C#
public record Person(string FirstName, string LastName){    public int Age { get; init; } // extra data, set with an object initializer} var p = new Person("Ada", "Lovelace") { Age = 36 };

Choosing

Positional is shorter and supports deconstruction and positional patterns, but the order is easy to get wrong with same-typed parameters. The property form names each value, handles optional ones, supports required, and takes attributes and validation per property. Validation in the positional form means redeclaring the property and putting the logic in its init accessor.

Mutability

A positional record class has init-only properties. A positional record struct has mutable ones unless it is declared readonly record struct. Both forms rely on init-only settersInit-only settersA property you can set while creating the object, and never again.Opens in a new tab.