C# 9.0 Init-Only Setters: Immutable Object Initialization

Init-only setters in C# 9 solve an age-old problem: how do you create immutable objects while still using the convenient object initializer syntax? Previously, you had to choose between constructor-based initialization (verbose) or public setters (mutable). Init-only setters give you both convenience and immutability.

The Problem

// Old approach 1: Mutable (bad for immutability)
public class Person
{
    public string Name { get; set; }  // Anyone can change anytime
}

var p = new Person { Name = "John" };
p.Name = "Changed";  // Oops, mutated!

// Old approach 2: Constructor only (inconvenient for many properties)
public class Person
{
    public string Name { get; }
    public string Email { get; }
    public DateTime DateOfBirth { get; }
    public string Address { get; }
    
    public Person(string name, string email, DateTime dob, string address)
    {
        Name = name;
        // ...constructor gets long and unwieldy
    }
}

The Solution: Init-Only Setters

public class Person
{
    public string Name { get; init; }
    public string Email { get; init; }
    public DateTime DateOfBirth { get; init; }
    public string Address { get; init; }
}

// Can use object initializer
var person = new Person
{
    Name = "John",
    Email = "john@example.com",
    DateOfBirth = new DateTime(1990, 1, 1),
    Address = "123 Main St"
};

// But cannot modify after initialization
person.Name = "Jane";  // Compile error!

Init in Derived Classes

Init-only properties can be set in derived class constructors too:

public class Employee : Person
{
    public string EmployeeId { get; init; }
    
    public Employee()
    {
        // Can set base class init properties
        Name = "Default";
    }
}

var emp = new Employee
{
    Name = "John",           // Still works
    EmployeeId = "E001"
};

Required Properties (C# 11 Preview)

Looking ahead, C# 11 will add required modifier to ensure init properties must be set:

public class Person
{
    public required string Name { get; init; }  // Must be set
    public string? Nickname { get; init; }       // Optional
}

var p = new Person();  // Error: Name is required
var p = new Person { Name = "John" };  // OK

Combining with Records

Records use init-only setters by default for positional properties, but you can also define additional init properties:

public record Person(string FirstName, string LastName)
{
    // Additional init-only property
    public string? MiddleName { get; init; }
}

var person = new Person("John", "Doe") { MiddleName = "William" };

Key Takeaways

  • Init-only setters allow object initializer syntax with immutability
  • Property can only be set during object initialization
  • Works with derived classes and records
  • Combine with required (C# 11) for mandatory properties

References


Discover more from C4: Container, Code, Cloud & Context

Subscribe to get the latest posts sent to your email.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.