Using the with Expression on Classes

Just today I learned that the with expression in C# is not limited to records. It can also be used with classes, as long as the class is defined with the with expression requirements. And what are the requirements to work with the with expression?

Requirements for with expressions to work:

  1. The type must be a reference type (class or record) or a struct.
  2. The type must be immutable in practice: meaning, all properties you’re trying to copy/replace must be:
    • init-only (init accessor instead of set)
    • Settable through a custom clone method, if you’re using a class.
  3. The type must have a Clone method accessible for the compiler to use behind the scenes.
  4. The with expression has been supported since .NET 5 / C# 9.

Records support with expressions out of the box

For example:

public record Person(string Name, int Age);

var p1 = new Person("Alice", 30);
var p2 = p1 with { Age = 31 }; // works!

Classes require more work to support

To use with on a class, you need to:

  1. Mark properties with init accessors.
  2. Implement a Clone() method (or override MemberwiseClone behavior).

Example:

public class Person
{
    public string Name { get; init; }
    public int Age { get; init; }

    public Person Clone() => (Person)this.MemberwiseClone();
}

Then you can use the with expression on the class:

var p1 = new Person { Name = "Alice", Age = 30 };
var p2 = p1 with { Age = 31 }; // works!

Conclusion

Applying the with expression to class-based objects allows you write immutable classes as with similar functionality as records. Sometimes you can’t use records but still want the benefit of creating immutable objects. Let an immutable class do the work for you.

Leave a comment