Last Updated on February 11, 2024


The C# init keyword is one of the new features available in C# 9.0, officially released on November 2020 with .NET 5.0.  This feature provides a way to initialize objects’ properties without being forced to put a set accessor on the properties. It’s a convenient solution when you need to implement a class with read-only or immutable properties. Before taking a closer look at the feature, let’s talk about the concept of read-only properties.

Immutable properties before C# 9.0

As a C# developer, there are many scenarios where you need to use immutable objects. Meaning, the state of the object should not be mutable during its lifecycle. The concept of immutability really makes the code safer and cleaner in a multi-threaded application. Moreover, it helps to address some of the concurrency issues that come with mutable states.

Therefore, in some use-cases, you want the class’ properties to remain the same after the initialization phase of the object. Usually, the initialization phase consists of calling the actual constructor of the class and/or using object initializers.

One of the downsides of object initializers is the fact that you need to be able to set the properties outside of the class itself. In such a case, you would add the set accessor on those properties as shown in the following example.

public class Movie
{
    public string Title { get; set; }
    public string DirectedBy { get; set; }
    public string Rating { get; set; }
}

And then callers can use object initializers to create the object:

Movie creed = new Movie() 
{ 
      Title = "Creed", 
      DirectedBy = "Ryan Coogler", 
      Rating = "PG-13" 
};

//then change the title of the object
creed.Title = "ABC";

Well, this code does not give us an object with immutable properties. We can set these properties again anytime after the initialization of the object.

 Basically, if we want to have immutable properties in this context, we need to add more code to the class declaration:

public class Movie
{
    private readonly string _title;
    private readonly string _directedBy;
    private readonly string _rating;

    public string Title { get =>_title;  }
    public string DirectedBy { get => _directedBy;  }
    public string Rating { get => _rating; }

    public Movie(string title, string directedBy, string rating)
    {
        _title = title;
        _directedBy = directedBy;
        _rating = rating;
    }
}

Then, we are able to use the constructor to create an instance of the class:

Movie creed = new Movie("Creed", "Ryan Coogler", "PG-13");

In this case, it’s obvious that the properties are immutable because the implementation contains only get accessors linked to read-only fields. This is an easy way to make sure that the properties keep the same values after the object’s initialization. Furthermore, if you try to change one of the read-only properties, you will receive a compile-time error:

read-only properties in C#

This example gives you an idea about the boilerplate code needed to implement read-only properties before C# 9.0. Now, let’s take a look at init-only properties.




The C# init keyword

In C# 9.0, there is a new keyword that gives the ability to implement read-only properties without having to write boilerplate code. It’s the init keyword. The difference between the init accessor and the set accessor is the fact that you can call the init accessor only during the object initialization (i.e constructor or initializers).

The following code shows the Movie class that we changed to add the init accessor on all properties:

public class Movie
{
    private readonly string _title;
    private readonly string _directedBy;
    private readonly string _rating;

    public string Title 
    { 
        get => _title;
        init => _title = value;
    }
    public string DirectedBy 
    { 
        get => _directedBy; 
        init => _directedBy = value; 
    }
    public string Rating 
    { 
        get => _rating;
        init => _rating = value;
    }

    public Movie()
    {
    }
    public Movie(string title, string directedBy, string rating)
    {
        _title = title;
        _directedBy = directedBy;
        _rating = rating;
    }
}

With this version of the class, we have 2 ways of initializing our immutable class. We can use the constructor or simply use object initializers.

Movie creed = new Movie() 
{ 
      Title = "Creed", 
      DirectedBy = "Ryan Coogler", 
      Rating = "PG-13" 
};

//or

creed = new Movie("Creed", "Ryan Coogler", "PG-13");

Personally, when creating POCO objects, I prefer using object initializers because it simplifies the code and makes it easier to read. For this reason, I would simply get rid of the constructor and the read-only fields. That gives us the following code.

public class Movie
{
     public string Title { get; init; }
     public string DirectedBy { get; init; }
     public string Rating { get; init; }
}

Then if you try to change one of the properties after initialization, you will receive this awesome compile-time error: “Init-only property or indexer ‘Movie.Title’ can only be assigned in an object initializer, or on ‘this’ or ‘base’ in an instance constructor or an ‘init’ accessor.

Properties validation

In order to validate the value assigned to an init-only property, we can use a read-only backing field as we did previously. This way, in the init accessor, we can check if the provided value is valid before assigning it to the read-only field. Here is an example of this implementation:

public class Movie
{
    private readonly string _title;
    private readonly string _directedBy;
    private readonly string _rating;

    public string Title
    {
        get => _title;
        init => _title = string.IsNullOrWhiteSpace(value) ? throw new ArgumentNullException(nameof(Title)) : value;
    }
    public string DirectedBy
    {
        get => _directedBy;
        init => _directedBy = string.IsNullOrWhiteSpace(value) ? throw new ArgumentNullException(nameof(DirectedBy)) : value;
    }
    public string Rating
    {
        get => _rating;
        init => _rating = string.IsNullOrWhiteSpace(value) ? throw new ArgumentNullException(nameof(Rating)) : value;
    }
}

With this implementation, if someone provides an invalid value during initialization, the code will just throw an ArgumentNullException.

Now, the question is: Is it possible to do a validation of the whole object after initialization? Meaning, verifying that the values assigned to the properties make sense as a whole and give a valid state to the object. Well, there is no straight forward solution for now. After doing, some research, I found Mads Torgesen’s comment on this topic.

Mads comment on init-only properties validation

So, it looks like Microsoft will find a solution for this scenario in C# 10.

C# init keyword and reflection

Now let’s take a look at the init-only properties from the perspective of developers who use .NET Reflection regularly.  Usually, I avoid using Reflection as much as possible. However, you may be in situations where you don’t have the choice but to use Reflection.

So, here is the code sample that I tried (don’t forget to add the System.Reflection namespace on top):

Console.WriteLine($"Start of Test");
Type movieType = typeof(Movie);
object obj = Activator.CreateInstance(movieType);
PropertyInfo[] props= movieType.GetProperties();
Console.WriteLine($"Here are the properties of the movie class:");
foreach (var prop in props)
{
    Console.WriteLine($"PropName:{prop.Name},Prop.CanRead:{prop.CanRead}, Prop.CanWrite:{prop.CanWrite}, Prop.Type:{prop.PropertyType} Prop.Value:{prop.GetValue(obj)}");
}
Console.WriteLine($"--------------------------------------");
Console.WriteLine($"Set values for all properties...");
foreach (var prop in props)
{
    prop.SetValue(obj, $"{prop.Name}Value1");
}
Console.WriteLine($"--------------------------------------");
Console.WriteLine($"Here are the properties of the movie class:");
foreach (var prop in props)
{
    Console.WriteLine($"PropName:{prop.Name},Prop.CanRead:{prop.CanRead}, Prop.CanWrite:{prop.CanWrite}, Prop.Type:{prop.PropertyType} Prop.Value:{prop.GetValue(obj)}");
}
Console.WriteLine($"--------------------------------------");

Console.WriteLine($"Trying to set values again for all properties....");
foreach (var prop in props)
{
    prop.SetValue(obj, $"{prop.Name}Value2");
}
Console.WriteLine($"--------------------------------------");
Console.WriteLine($"Here are the new values for properties of the movie class:");
foreach (var prop in props)
{
    Console.WriteLine($"PropName:{prop.Name},Prop.CanRead:{prop.CanRead}, Prop.CanWrite:{prop.CanWrite}, Prop.Type:{prop.PropertyType} Prop.Value:{prop.GetValue(obj)}");
}
Console.WriteLine($"End of Test");

This  code uses the following definition of the movie class:

public class Movie
{
     public string Title { get; init; }
     public string DirectedBy { get; init; }
     public string Rating { get; init; }
}

And here is the output of the console app when you run the code:

In this output, you can clearly see that we are able to set the properties twice. Therefore, it is possible to set the properties as many times as you like if you are using Reflection. This makes sense because the init-only properties are a C# language feature, therefore a compile-time feature, not a runtime feature.

Summary

In a nutshell, init-only properties allow you to define classes with immutable properties without having to write a lot of boilerplate code with read-only fields and constructors. This feature is very practical to use when working with immutable data. In my opinion, it should be used with POCO objects because most of the time, these objects are created by deserializing data coming from another component and you want to make sure that these objects are not modified by mistake in your business logic code.

Furthermore, if you need the whole object to be immutable, you can use init-only properties with record types in C# 9.0.

I hope you enjoyed this article from our fundamentals series. You can find other articles with the fundamental tag here