Search code examples
c#cloneimmutabilityicloneable

Change property of immutable type


I have stored immutable types in a temporary CQRS read store (query/read side, in fact implemented by a simple List with abstraction access layer, I don't want to use a full blown document database at this point). These read stores contains items like the following:

public class SomeItem
{
    private readonly string name;
    private readonly string description;

    public SomeItem(string name, string description)
    {
        this.name = name;
        this.description = description;
    }

    public string Name
    {
        get { return this.name; }
    }

    public string Description
    {
        get { return this.description; }
    }
}

Now I want to change the Name and in a 2nd Command the Description. These changes should keep the current state, which means for the example above:

// initial state
var someItem = new SomeItem("name", "description");

// update name -> newName
someItem = new SomeItem("newName", someItem.Description);

// update description -> newDescription
someItem = new SomeItem(someItem.Name, "newDescription");

This does look error prone to me if you have several properties... you have to manage keeping the current state. I could add something like Clone() to every type but I think/hope there is something better out there that performs well and is easy to use, I don't want to write much repetive code (lazy programmer). Any suggestions how to improve the code above? The SomeItem class needs to stay immutable (transported through several different threads).


Solution

  • With C#9 we got the with operator for this purpose.

       public record Car
        {
            public string Brand { get; init; }   
            public string Color { get; init; }    
        }
        var car = new Car{ Brand = "BMW", Color = "Red" }; 
        var anotherCar = car with { Brand = "Tesla"};
    

    With-expressions When working with immutable data, a common pattern is to create new values from existing ones to represent a new state. For instance, if our person were to change their last name we would represent it as a new object that’s a copy of the old one, except with a different last name. This technique is often referred to as non-destructive mutation. Instead of representing the person over time, the record represents the person’s state at a given time. To help with this style of programming, records allow for a new kind of expression; the with-expression:

    News in C#9

    NOTE With operator is only supported by records.

    Records At the core of classic object-oriented programming is the idea that an object has strong identity and encapsulates mutable state that evolves over time. C# has always worked great for that, But sometimes you want pretty much the exact opposite, and here C#’s defaults have tended to get in the way, making things very laborious.

    Recods in C#9