Setting readonly variable using init

Readonly variable can be set using an init only setter

Home DailyDrop

Daily Knowledge Drop

Readonly variables on a class can be set, not only in the constructor, but also using the init keyword.


Before init

Prior to the introduction of the init keyword in C#9, if a class had a readonly variable, its value had to be set either:

  • when declared
  • in the constructor of the class

Consider the following class:

public class Song
{
    // if not explicitly set, this variable will have 
    // default int value
    public readonly int Id;

    // explicitly set the value when declared
    public readonly DateTime DateCreated = DateTime.Now;

    public string Name { get; set; }

    // The readonly Id field is set in the constructors
    public Song()
    {
        Id = 0;
    }

    // set both values in the constructor
    public Song(int id, string name)
    {
        Id = id;
        Name = name;
    }
}

When using an instance, as expected, the value cannot be set:

var song = new Song();
//song.Id = 100; << This results in an error

The issue with this, is that if you want to set the value of Id, you cannot use the object initialization format. The only way to set the value is using the constructor

var song2 = new Song
{
    Name = "Song2"
    // Id =.. << Id is not available to set here
};

// This is the only way to set the Id
var song3 = new Song(17, "Song3");

The init keyword

The init keyword was introduced in C#9, which allows for the ability for a property to be set, but only on initialization.

Here is the same Song class, but with the Name property changed to use init instead of set:

public class Song
{
    // if not explicitly set, this variable will have 
    // default int value
    public readonly int Id;

    // explicitly set the value
    public DateTime DateCreated { get; init; }

    public string Name { get; init; }

    // set the value in the constructor
    public Song()
    {
        Id = 0;
    }

    // set both values in the constructor
    public Song(int id, string name)
    {
        Id = id;
        Name = name;
    }
}

An example of this in action:

// Allowed
var song = new Song(17, "Song");

// All good
var song2 = new Song
{
    Name = "Song2"
};

//song2.Name = "Song3" << NOT allowed

Readonly and init

While readonly variables still cannot use the init keyword, they can still be set when other properties are initialized.

In the below Person class example, the readonly Age is calculated and set when the DateOfBirth property is set:

public class Person
{
    private DateTime _dateOfBirth;

    public string Name { get; set; }

    public readonly int Age;

    public DateTime DateOfBirth
    {
        get => _dateOfBirth; 
        init
        {
            _dateOfBirth = value;
            // Set the value of the readonly field
            Age = (int.Parse(DateTime.Now.ToString("yyyyMMdd")) - 
                    int.Parse(_dateOfBirth.ToString("yyyyMMdd"))) 
                    / 10000;
        }
    }
}

Object initialization can now be used:

var person = new Person();
Console.WriteLine(person.Age);

var person2 = new Person
{
    Name = "Person1",
    DateOfBirth = new DateTime(1983, 08, 25)
};
Console.WriteLine(person2.Age);

With the output being:

0
38

Limitations

As shown in the above example, there are some limitations when using this approach:

  • The readonly field can only be set if another property has an init only setter
  • The property with the init only setting, needs to be converted to have a private backing field, and the get of the property now needs to be manually done

Notes

While not especially useful in every use case, when the need does arise, the ability to calculate and set a readonly property based on the initialization of another field, can prove to be very useful.
Even though there is some modifications required to the class (as mentioned in the above limitations), implementing this requirement without the init keyword would result in even more code.


References

Init Only Setters

Daily Drop 48: 08-04-2022

At the start of 2022 I set myself the goal of learning one new coding related piece of knowledge a day.
It could be anything - some.NET / C# functionality I wasn't aware of, a design practice, a cool new coding technique, or just something I find interesting. It could be something I knew at one point but had forgotten, or something completely new, which I may or may never actually use.

The Daily Drop is a record of these pieces of knowledge - writing about and summarizing them helps re-enforce the information for myself, as well as potentially helps others learn something new as well.
c# .net readonly init