Menu
Coddy logo textTech

Immutability Patterns

Part of the Object Oriented Programming section of Coddy's C# journey — lesson 34 of 70.

Immutability takes encapsulation to its logical extreme - once an object is created, it can never change. This eliminates entire categories of bugs because you never have to worry about something unexpectedly modifying your data.

The simplest way to create an immutable class is to use readonly fields with a constructor that sets all values, and provide no setters:

public class Point
{
    public readonly int X;
    public readonly int Y;
    
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}

Once a Point is created, its coordinates cannot change. But what if you need a modified version? Instead of changing the existing object, you return a new one:

public Point Move(int deltaX, int deltaY)
{
    return new Point(X + deltaX, Y + deltaY);
}

The original point stays unchanged while you get a new point with updated values. This pattern is used throughout .NET - strings, for example, are immutable. When you call ToUpper(), you get a new string rather than modifying the original.

Immutable objects are inherently thread-safe since no code can modify them after creation. They're also easier to reason about because you know their state will never change unexpectedly.

challenge icon

Challenge

Easy

Let's build an immutable money system that demonstrates how objects can be designed to never change after creation. You'll create a Money class where every operation returns a new object instead of modifying the original - a pattern that makes your code safer and easier to reason about.

You'll organize your code across two files:

  • Money.cs: Create an immutable Money class in the Finance namespace. Your money should have:
    • Two readonly fields: Amount (decimal) and Currency (string)
    • A constructor that sets both values - once created, a Money object can never change
    • A method Add(decimal value) that returns a new Money object with the increased amount (same currency)
    • A method Subtract(decimal value) that returns a new Money object with the decreased amount (same currency)
    • A method Convert(string newCurrency, decimal rate) that returns a new Money object with the amount multiplied by the rate and the new currency
    • A method GetDisplay() that returns a string in the format "{Amount} {Currency}"
    Remember: none of these methods should modify the original object - they all return brand new Money instances!
  • Program.cs: In your main file, demonstrate immutability in action. Create an initial Money object, then perform operations on it while keeping references to both the original and the results. This will prove that the original never changes even after multiple operations.

You will receive four inputs:

  • Initial amount (decimal)
  • Initial currency (string)
  • Amount to add (decimal)
  • Conversion rate to EUR (decimal)

Print the output in this format:

Original: {GetDisplay()}
After add: {GetDisplay()}
Original unchanged: {GetDisplay()}
Converted: {GetDisplay()}

For example, if the inputs are 100.00, USD, 50.00, and 0.85, the output should be:

Original: 100.00 USD
After add: 150.00 USD
Original unchanged: 100.00 USD
Converted: 127.50 EUR

Notice how the original Money object still shows 100.00 USD even after calling Add() - that's immutability! The Add() method created a completely new object with 150.00 USD, leaving the original untouched. The conversion is performed on the result of the add operation.

Cheat sheet

Immutability means that once an object is created, it can never change. This eliminates bugs related to unexpected data modifications.

Create an immutable class using readonly fields with a constructor and no setters:

public class Point
{
    public readonly int X;
    public readonly int Y;
    
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}

To modify an immutable object, return a new instance instead of changing the existing one:

public Point Move(int deltaX, int deltaY)
{
    return new Point(X + deltaX, Y + deltaY);
}

Benefits of immutable objects:

  • Thread-safe by design since no code can modify them after creation
  • Easier to reason about because their state never changes unexpectedly
  • Used throughout .NET (e.g., strings are immutable)

Try it yourself

using System;
using Finance;

class Program
{
    public static void Main(string[] args)
    {
        // Read inputs
        decimal initialAmount = Convert.ToDecimal(Console.ReadLine());
        string initialCurrency = Console.ReadLine();
        decimal amountToAdd = Convert.ToDecimal(Console.ReadLine());
        decimal conversionRate = Convert.ToDecimal(Console.ReadLine());

        // TODO: Create an initial Money object with the initial amount and currency

        // TODO: Print the original money using GetDisplay()

        // TODO: Call Add() on the original and store the result in a new variable

        // TODO: Print the result after add using GetDisplay()

        // TODO: Print the original again to prove it's unchanged

        // TODO: Convert the added result to EUR using the conversion rate

        // TODO: Print the converted money using GetDisplay()
    }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming