Published on

C# Records and the with Keyword — Update without mutation

Authors

The with Keyword for C# Records

C# Records is a very useful feature that has been added to the language. Been playing around with it a lot recently and stumbled upon the with keyword. It is very useful when you need to update a few properties in an immutable object, without changing the original object. This approach is also known as non-destructive mutation.


So, why does it matter?

With records, the data is immutable — once you create the object, its values stay fixed. This characteristic helps maintain strong data integrity.

Example without with:

public record Product(string Name, decimal Price, string Category);

var original = new Product("Laptop", 999.99m, "Electronics");

// Old way: recreate with updated values
var updated = new Product(
    original.Name,
    900.99m,
    original.Category
);

With with, it’s much simpler:

var updated = original with { Price = 899.99m };

The compiler clones the existing object using a hidden method, copies all property values, and applies the specified changes. The original object remains the same, due to it being immutable


Updating multiple properties.

var clearanceItem = original with
{
    Price = 749.99m,
    Category = "Clearance"
};

Changing nested records.

public record Address(string Street, string City);
public record Customer(string Name, Address Location);

var customer = new Customer("Jane", new Address("123 Main St", "Springfield"));

var movedCustomer = customer with
{
    Location = customer.Location with { City = "Shelbyville" }
};

Use cases

For DDD, value objects are immutable. The with keyword makes updates very simple.


public record class Price
{
    public decimal Amount { get; init; }
    public string Currency { get; init; }

    public Price(decimal amount, string currency)
    {
        if (amount < 0)
            throw new ArgumentOutOfRangeException(nameof(amount), "Amount must be positive.");
        if (string.IsNullOrWhiteSpace(currency))
            throw new ArgumentException("Currency is required.", nameof(currency));

        Amount = amount;
        Currency = currency;
    }
}

var price = new Price(Amount: 100m, Currency: "USD");
var updatedPrice = price with { Amount = 120m };

Limitations

  • Works only on records (class or struct)
  • Properties must have init or set accessors
  • You cannot use it on normal c# classes. Then you would have to write your own clone logic.
  • Each with call will create a new object. Be aware of the overhead for performance-sensitive code.

Summary: The with keyword is awesome. It makes working with immutable records straightforward. It is one of the many new c# features that make the language modern and up to date.