Records, Anonymous Types, and Non-Destructive Mutation

Records, Anonymous Types, and Non-Destructive Mutation

2 min read ·

Today, I'm going to share some fascinating things you can do with records and anonymous types. I will introduce you to the concept of non-destructive mutation. And I will talk about when and why we might want to use this C# language feature.

What Is a Record?

With C# 9 we can use records that are a new reference type. C# 10 introduced record structs so that you can define records as value types. Records are distinct from classes in that record types use value-based equality.

Let's see how we would define a record:

public record Food(string Name, double Price);

This way of declaring a record is called a positional record. The constructor we have defined here is called the primary constructor.

The Name and Price properties are init only properties. This means they can only be set in the constructor or using a property initializer.

Since our properties are init only, is there any way to change their value?

Non-Destructive Mutation Using The With Expression

We said we can't modify the properties of our record, because the properties are init only. However, we can use the with expression (introduced in C# 9) to create a new instance of our record with modified values.

Let's see how we would use the with expression:

var banana = new Food("🍌", 1.95);

var bananaOnSale = banana with
{
    Price = 0.99
};

It's important to highlight two things here:

  • The original banana instance remains unchanged
  • The with expression creates a new record instance with only the Price property modified

I mentioned Anonymous Types in the title, so let me show you something interesting you can do with them.

Anonymous Types And Non-Destructive Mutation

Did you know that you can use the with expression with anonymous types?

Just a reminder that the with expression is available from C# 9 and later.

Let's create an anonymous type:

var apple = new
{
    Name = "🍎",
    Price = 1.21
};

This is how we can modify it using the with expression:

var orange = apple with
{
    Name = "🍊"
};

And again the same rules apply:

  • The original apple instance remains unchanged
  • The with expression creates a new anonymous type instance with only the Name property modified

I found this feature useful in LINQ method chains.

For example, loading an anonymous type from the database where some properties have a default value. You can then use this feature to calculate the values for these properties in memory.


Whenever you're ready, there are 4 ways I can help you:

  1. (COMING SOON) REST APIs in ASP.NET Core: You will learn how to build production-ready REST APIs using the latest ASP.NET Core features and best practices. It includes a fully functional UI application that we'll integrate with the REST API. Join the waitlist!
  2. Pragmatic Clean Architecture: Join 3,600+ students in this comprehensive course that will teach you the system I use to ship production-ready applications using Clean Architecture. Learn how to apply the best practices of modern software architecture.
  3. Modular Monolith Architecture: Join 1,600+ engineers in this in-depth course that will transform the way you build modern systems. You will learn the best practices for applying the Modular Monolith architecture in a real-world scenario.
  4. Patreon Community: Join a community of 1,000+ engineers and software architects. You will also unlock access to the source code I use in my YouTube videos, early access to future videos, and exclusive discounts for my courses.
  5. Promote yourself to 60,000+ subscribers by sponsoring this newsletter.

Become a Better .NET Software Engineer

Join 60,000+ engineers who are improving their skills every Saturday morning.