$$ \newcommand\Tr{\mathrm{Tr}} \newcommand{\braket}[2]{\langle #1 \mid #2 \rangle} \newcommand\I{\mathbb{I}} \newcommand{\avg}[1]{\left< #1 \right>} \newcommand{\RD}{D} \newcommand{\ri}{\mathrm{i}} \DeclareMathOperator{\sign}{sign} \DeclareMathOperator{\Sign}{Sign} \newcommand{\ii}{\mathrm i} \newcommand{\vv}{\mathrm v} \newcommand{\ff}{\mathrm f} \newcommand{\mm}{\mathrm m} \newcommand{\ee}{\mathrm e} \newcommand{\xx}{\mathrm x} \newcommand{\RR}{\mathrm R} \newcommand{\dd}{\mathrm d} \newcommand{\FF}{\mathrm F} \newcommand{\BB}{\mathrm B} \newcommand{\vph}{v_{\mathrm{ph}}} $$

Init-only Setter

Overview

Init-only setter is a new feature in C# 9.0. It allows us to set the value of properties during object initialization using object initializer syntax, but after object construction completes, the properties become read-only, hence immutable.

Below is the example of Init-only setter implementation.

internal class Program
{
    static void Main(string[] args)
    {
        Person person = new Person { Name = "Michael" };
    }
}

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

    public Person()
    {
    }

    public Person(string name)
    {
        Name = name;
    }
}

Why do we need Init-only setter

Previously, we can create immutable(read-only) property using getter-only property. But, in this way we cannot create object using object initializer syntax. We must create object through custom constructor that takes parameter.

internal class Program
{
    static void Main(string[] args)
    {
        // Compile error, because object can't be created through object initializer syntax
        Book book = new Book { ISBN = "1", Title = "How to cook", Author = "Michael" };

        // It will work
        Book book = new Book("1", "How to cook", "Michael");        
    }
}

public class Book
{
    public string ISBN { get; }
    public string Title { get; }
    public string Author { get; }

    public Book(string isbn, string title, string author)
    {
        ISBN = isbn;
        Title = title;
        Author = author;
    }
}

We also cannot use private setter as an alternative because:

  1. It can’t be set from outside the class
  2. It breaks immutability of the object because after object creation, the value can be changed in other methods in the class

So, that’s why Init-only setter exists since C# 9.0. Adding init setter as well as default constructor will allow use to create an immutable(read-only) object using object initializer syntax.

internal class Program
{
    static void Main(string[] args)
    {
        // It will work now
        Book book = new Book { ISBN = "1", Title = "How to cook", Author = "Michael" };

        // It will also work
        Book book = new Book("1", "How to cook", "Michael");
    }
}

public class Book
{
    public string ISBN { get; init; }
    public string Title { get; init; }
    public string Author { get; init; }

    public Book()
    {
    }

    public Book(string isbn, string title, string author)
    {
        ISBN = isbn;
        Title = title;
        Author = author;
    }
}