Init-Only Properties In C# 9

Getting Setup With C# 9

If you aren’t sure you are using C# 9 and/or you want to start using some of the new shiny features in the C# language, be sure to read our quick guide on getting setup with C# 9 and .NET 5. Any feature written about here is available in the latest C# 9 preview and is not a “theoretical” feature, it’s ready to go!

C# 9 Features

We are slowly working our way through all new C# 9 features, if you are interested in other new additions to the language, check out some of the posts below.

What We Have Currently

So before we jump into C# 9 and Init Only Properties, I thought let’s take a quick look on the problem this feature is actually trying to solve. In some classes, typically “model” classes, we want to make properties publicly readable, but not be able to be set outside our class. So something like this is pretty common :

public class Person
{
    public string Name { get; private set; }
}

We can now set the property of Name anywhere inside the class, for example in a constructor or from a method pretty easily :

public class Person
{
    public Person(string name)
    {
        this.Name = name;
    }

    public void SetName(string name)
    {
        this.Name = name;
    }

    public string Name { get; private set; }
}

But what’s harder now is that we can’t use an Object Initializer instead of a constructor, even if we are “newing” up the object for the first time, for example :

var person = new Person
{
    Name = "Jane Doe" // Compile Error 
};

No bueno! We get the following error :

The property or indexer 'Person.Name' cannot be used in this context because the set accessor is inaccessible

It’s not the end of the world but pretty annoying none the less.

But there’s actually another bigger issue. That is that in some cases, we may want a property to be set inside a constructor *or* object initialization and then not be changed after being created. Essentially, an immutable property.

There’s actually nothing that stops us from modifying the property after creation from inside the class. For example a private method can be called to edit the property just fine like so :

public class Person
{
    public void SetName(string name)
    {
        this.Name = name;
    }

    public string Name { get; private set; }
}

And that’s the larger problem here. I think for many years C# developers have used “private set;” as a way to half achieve immutability by only setting that property within a constructor or similar, and that sort of works for libraries where you don’t have access to the actual source code, but there’s nothing actually signalling the intent of the original developer that this property should *only* be set on object creation and never again.

Introducing Init-Only Properties

Let’s take our Person class and modify it like so :

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

Notice how we change our “set” to “init”. Pretty easy, now let’s look at how that might affect code :

var person = new Person();
person.Name = "Jane Doe"; // Compile Time Error

Immediately the compiler throws this error :

Init-only property or indexer 'Person.Name' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor

OK so far, pretty similar to the “private set” we were using before. But notice that object initializers now do work :

var person = new Person
{
    Name = "Jane Doe" // Works just fine!
};

How about in the constructor?

public class Person
{
    public Person(string name)
    {
        this.Name = name;
    }

    public string Name { get; init; }
}

Also compiles fine! And what about in a method on a class :

public class Person
{
    public void SetName(string name)
    {
        this.Name = name; // Compile Error
    }

    public string Name { get; init; }
}

Boom! Everything blows up. So we’ve got that immutability we’ve always been after with private set, but it’s actually enforced this time!

Init is a relatively small change on the face of it, it’s just an extra keyword for properties, but it does make complete sense why it’s been added and I’m sure will be a welcome change for all C# developers.

Comparison With Readonly Variables

So I know someone on Twitter will definitely shoot me down for saying that we “finally” have immutable properties when we’ve had the “readonly” keyword for quite some time now. But there’s some differences that really make the init change a huge quality of life addition.

So this does create an “immutable property” :

public class Person
{
    public Person(string name)
    {
        _name = name;
    }

    private readonly string _name;
    public string Name => _name;
}

However you must now use the constructor, you cannot do this using object initializers :

var person = new Person
{
    Name = "Jane Doe"
};

Also the fact that you have to create a backing variable for the property is rather annoying so… I still love the init addition.

Side note. Someone pointed out in the comments that you can create a hidden backing field just by adding a single get accessor :

public class Person
{
    public string Name { get; } 
    
    Person(string name) 
        => Name = name;
} 

So this also works and is pretty close to immutability, but again, no object initializer (And no really signally the intent I think). But shout out to Antão!

I should also note that init properties actually work well with readonly variables if you actually do need it since init is only run at object construction which fits the readonly paradigm just fine. So as an example, this compiles and runs fine :

public class Person
{
    private readonly string _name;

    public string Name
    {
        get => _name;
        init => _name = value;
    }
}

Troubleshooting

If you get the following error :

The feature 'init-only setters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.	

It means you are using a version of C# less than 9 (Or atleast not with the latest preview version). Check out our quick guide on getting setup with using C# 9 and all it’s features here : https://dotnetcoretutorials.com/2020/08/07/getting-setup-with-c-9-preview/

ENJOY THIS POST?
Join over 3.000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.

2 comments

Leave a Reply

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