Newer .NET developers are sometimes confused when they encounter readonly members in classes and the newer init-only setters. In this article we’ll explore both keywords along with the related get-only auto property. Finally, we’ll help you understand when to use each of these powerful language features.
The C# Readonly Keyword
The readonly keyword states that the field that it is applied to cannot be changed after the class is created.
Let’s take a look at a sample readonly field:
private readonly int birthYear = 1998;
Here the birthYear
field is given a value of 1998 and cannot be changed to a different value elsewhere in the class without generating a compiler error.
The advantages of the readonly
keyword are the following:
- It makes it clearer that a value should never change
- It allows the compiler to make some performance optimizations knowing that the value will never change
- It can protect you from future mistakes if you need to make sure a value never changes.
I find myself using readonly
frequently when a class manages a List
, Dictionary
, or other class to accomplish its goals.
Important Note: Many people incorrectly assume that readonly
means that the object it is attached to becomes unchangeable or immutable. For example, they assume a List<string>
defined as readonly
cannot add new items to it. This is not the case. All readonly
says is that the field holding the list cannot be updated to point to a different List<string>
on the heap.
Get-only Auto-Properties
Let’s talk briefly about get-only auto properties in C# because they’re really just a fancy way of using readonly
under the hood.
Take a look at the following class that uses a get-only property called Color
:
public class Ball
{
public Ball(string color)
{
this.Color = color;
}
public string Color {get;} // get-only auto-property
}
In this code, once the Ball
is created, its Color
property cannot be changed. This is because the code above is roughly equivalent to the following code:
public class Ball
{
private readonly string color;
public Ball(string color)
{
this.color = color;
}
public string Color
{
get
{
return color;
}
}
}
Here we see that get-only auto properties automatically create a readonly
field to manage their data.
Init Only Setters
Init-only setters are a newer language feature that give you the ability to set read-only properties of a class at construction without needing to add constructor parameters.
Take the following class that doesn’t use init-only setters:
public class Pet
{
public Pet(string breed, int birthYear, string name)
{
}
// Get-only auto properties
public string Breed {get;}
public int BirthYear {get;}
public string Name {get;}
}
Here we can create a new Pet
with code like the following:
Pet myPet = new Pet("Cairn Terrier", 2016, "Jester");
This is fine, but as the number of properties we want to set grows, the number of parameters to the constructor has to grow too.
To help with this problem, C# gives us the option to use init-only property setters that can only be invoked when creating a class.
Here’s a version of Pet
that takes advantage of init-only setters:
public class Pet
{
public string Breed {get; init;}
public int BirthYear {get; init;}
public string Name {get; set;}
}
Note that this class doesn’t declare a constructor beyond the default empty constructor. Admittedly this is a very simple class that perhaps should be a stuct
or a record
, but simple is fine for articles.
We could create an instance of Pet
with the following code that provides an initializer after the constructor call:
Pet myPet = new Pet()
{
Breed = "Cairn Terrier",
BirthYear = 2016,
Name = "Jester"
};
This has the same effect as the code we saw before, but the initializer allows us to make our code more readable as the number of properties we need to initialize grows.
After the class is created, you will not be able to modify Breed
, BirthYear
, or Name
because they are init-only.
Init-only setters are something that you might not use frequently, but they offer an alternative to passing large numbers of parameters to constructors, which can simplify your classes and make their creation more readable as well.
Should I use Readonly, Get-only auto properties, or Init Only Setters?
Everything in this article is what I would consider to be an optional language feature of C#. You can use readonly
, get-only auto properties, and init-only setters, but you never have to. Of course, all three of these language features will make your code more readable in its intent, slightly more performant, and can protect against undesired behavior for values you don’t want to change after creation.
If you do want to enforce that a value won’t change after an object is created, either readonly
, a get-only auto property, or an init-only setter would work for you. So which one is right?
For me, I use the following decision-making process:
- If I want to store a value but don’t need to expose it as a property, I use a
readonly
field. - If I have a small number of required attributes for a class and don’t expect that list to grow, I use a get only auto-property.
- If I have a large number of properties I want to set or expect my property list to grow significantly over time, I use an init-only setter.
Bear in mind, as always, that the more features of a language you use the greater your code’s learning curve will be. This is especially true for newer and less-frequently used aspects of a language.
Still, these language features help your intent be more readable, your code more performant, and reduce the amount of boiler-plate you use in your codebases.
What do you and your team use, and why?
Top comments (4)
Muy buen post <3
Merci!
Very good explanation!
Name field isn't marked as init, it can be changed