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
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.