C# Records and How To Use Them

Date: 2025-07-18 | build | create | csharp | tech |

DISCLOSURE: If you buy through affiliate links, I may earn a small commission. (disclosures)

Records are thin data types that typically have value-based equality and are immutable. I like using them in most programming languages as I think they help keep data and behavior separate which often leads to more testable and composable systems.

Here we'll walk through using records in C#.

How to write records in C#

To declare a record, we can use the record keyword.

Here we define a simple Person record that has a first name and last name:

public record Person(string firstName, string lastName);

This lets us create records as if we were calling a class constructor (that's what's happening under the hood). Because it's a constructor, we can also create records with named parameters - this is what I recommend to avoid accidentally setting the wrong field if you try to refactor this code.

var a = new Person("HAMY", "LABS");
var a_clone = new Person("HAMY", "LABS");
var b = new Person("NOT", "HAMY");

var c = new Person(lastName: "This is", firstName: "okay");

Because these are records, we get value-based equality out of the box. This means that comparing two records with the same values in all fields will render them equal as opposed to C# class's typical object id check.

// Prints True
Console.WriteLine($"a == a_clone: {a == a_clone}");

// Prints False
Console.WriteLine($"a == b: {a == b}");

We'll note that because these fields are immutable, we cannot change them directly. However we can create new records from existing records with some values changed using the with keyword.

// Not allowed cause immutable
// c.lastName = "New name";

// Is allowed because creating a new record, not modifying old one
var cNewLastName = c with { lastName = "New name" };

Finally records come with built in formatting so they're pretty easy to debug by printing to console:

// Prints: Person { firstName = okay, lastName = This is }
Console.WriteLine($"c: {c}");

// Prints: Person { firstName = okay, lastName = New name }
Console.WriteLine($"cNewLastName: {cNewLastName}");

One final note is that records can inherit from other records. But you probably shouldn't do that. In general you should favor composition over inheritance and that's especially true when all we're doing is modeling data. So if you feel yourself reaching for inheritance for records do a double then a triple take to make sure there's not a simple workaround before committing.

Next

I generally prefer a separation of data and behavior and records suit that purpose far better than classes in C#. Records are my first choice if I'm trying to model or pass data around and I only reach for classes if there's some additional behavior I think would be useful to be attached to that data specifically. Typically there's not so for most cases records and functions do most of the work.

The full demo code is here in the blog post but if you want the full project files, you can join HAMINIONs and get access to the full source code (GitHub). By joining HAMINIONs you get access to dozens of example projects and support me in building future projects and tutorials.

If you liked this post you might also like:

Want more like this?

The best way to support my work is to like / comment / share for the algorithm and subscribe for future updates.