Home > dotnet > Comparing Init-Only, Get-Only, and Readonly in C# Code

Comparing Init-Only, Get-Only, and Readonly in C# Code

Exploring 3 different ways of declaring unchanging state in C# fields & properties

by

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 in C#

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:

  1. If I want to store a value but don’t need to expose it as a property, I use a readonly field.
  2. 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.
  3. 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?

Author

  • Matt Eland

    After several decades as a software engineer and engineering manager, Matt now serves as a software engineering instructor at Tech Elevator where he gets to raise up future developers and unleash them upon the world to build awesome things. Matt is an Azure Data Scientist and AI Engineer Associate, runs a data science blog and YouTube channel, is currently pursuing a master's degree in data analytics, and helps organize the Central Ohio .NET Developer Group. In his copious amounts of spare time, Matt continues to build nerdy things and looks for ways to share them with the community.

2 comments

Gene Huh October 23, 2022 - 1:00 am

My team works almost exclusively in the backend. We use read-only when assigning an injected service or Configuration into singleton services. These are auxiliary like logger and metrics. Most of the objects are instantiated on JSON deserialization, so we use get-only for the properties. Interestingly, for the objects we serialize, we could use the init-only instead of the default constructor and allowing those properties to be public settable.

Of course in all things, it depends on use case because there can be an expectation to be able to change a property after its creation, but that is a nice way to keep the constructor simple.

Reply
Introducing the C# 11 Required Keyword November 12, 2022 - 10:56 pm

[…] feature, like init-only properties and a number of previous language features, is aimed at improving the way we initialize objects […]

Reply

Leave a Reply

Related Content

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More