Preview of c# 9.0 new features – init only attribute

Time:2021-12-7

Preview of c# 9.0 new features – init only attribute

preface

As the release date of. Net 5 approaches, the corresponding c# new version has been determined as c# 9.0, and the newly added features (or syntax sugar) have been basically locked. This series of articles will show you them.

catalogue

[C# 9.0 new features Preview – new for type derivation]
[C# 9.0 preview of new features – null parameter verification]
[C# 9.0 new features Preview – top level statement]
[c# 9.0 new features Preview – init only attribute]
[c# 9.0 new features Preview – record type]
[preview of c# 9.0 new features – improvement of pattern matching]
[c# 9.0 new features Preview – source code generator]
[c# 9.0 new features Preview – other small changes]

Initialize setters only (init only setters)

This feature allows the creation of init only attributes and indexers, making the immutable model in c# more flexible.

background

Before that, when we created model classes such as entity class / poco class / dto class, we expected the property to be read-only and not allowed to be modified externally. We would set the property setter to private or not set it at all, for example:

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

Add another constructor with all attributes as signature:

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

Although this can achieve the goal, it brings two problems
1. If the attributes are increased, the workload will be doubled and difficult to maintain
2. Object initializers cannot be used

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

In this case, the init keyword came into being.

grammar

The syntax is very simple. You only need to replace the set keyword in the attribute with init:

public string Name { get; init; }

The above code will be roughly translated as:

private readonly string _name;
public string Name
{
    get { return _name; }
    set { _name = value;}
}

As you can see, the difference from set is that init will add the readonly keyword to the fields behind it.
In this way, we can get rid of the construction method of a pile of attributes and use the object initializer instead, and achieve the purpose of immutability.

var person = new Person
{
    Name = "Rwing"
};
//It cannot be modified again after initialization
person.Name = "Foo"; // Error: Name is not settable

This syntax needs to be used in many scenarios in conjunction with the record type also added in c# 9.0.

What conditions can be set

  • Through object initializer
  • With expression
  • In the constructor of itself or a derived class
  • In the attribute marked init
  • In the named parameter attribute of the attribute class

The above scenario is not difficult to understand, but it is worth mentioning that only the get attribute cannot be assigned in the constructor of the derived class, but init can:

class Base
{
    public bool Foo { get; init; }
    public bool Bar { get; }
}

class Derived : Base
{
    Derived()
    {
        Foo = true;
        Bar = true; // ERROR
    }
}

In addition, there is an exception. Lambda or local functions in the above scenarios are not allowed to be set, for example:
The reason is also very simple. Lambda or local functions are no longer in the constructor after compilation.

public class Class
{
    public string Property { get; init; }
    
    Class()
    {
        System.Action a = () =>
        {
            Property = null; // ERROR
        };
        local();
        void local()
        {
            Property = null; // ERROR
        }
    }
}

reference resources

[Proposal: Init Only Setters]
[InitOnlyMemberTests.cs]