Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

The C# 9 init-only Setters

3.65/5 (5 votes)
15 Aug 2021CPOL2 min read 8.2K  
A discussion of init keyword introduced in C# 9
This post discusses the somewhat new keyword introduced in C# 9 with .NET 5, which is the init keyword.

In a previous post in which we discussed about the Null Object Pattern, we discussed of a hackish approach to make sure that the default properties cannot be changed.

Please note that the code snippets from this post have been run in LinqPad so they can be used as is, to run them in Visual Studio, you might be required to make small changes.

In this post, we will discuss about the somewhat new keyword that was introduced in C# 9 with .NET 5, and that is the init keyword.

How Does It Work?

This keyword will allow us to create immutable objects (at least in regards to the properties that are marked with init) so that we can make sure no one can change the value later on.

So how do we use this keyword? Well, this keyword is to be used instead of the set keyword when declaring properties as follows:

C#
class TestClass1
{
	public int TestClass1IntProperty { get; set; }
}

class TestClass2
{
	public int TestClass1IntProperty {get; init; }
}

And the usage of these properties can be seen as follows:

C#
void Main()
{
	TestClass1 test1 = new TestClass1
	{
		TestClass1IntProperty = 42
	};
	
	test1.TestClass1IntProperty = 43; // this is ok because the property is mutable.
	
	TestClass2 test2 = new TestClass2
	{
		TestClass2IntProperty = 42
	};
	
	// this will show the following compiler error: 
	// CS8852 Init-only property or indexer '<full name of property>'
	// can only be assigned to an object initializer,
	// or on 'this' or 'base' in an instance constructor or an 'init' accessor.
	test2.TestClass2IntProperty = 43; 
}

Because this keyword is used instead of the set keyword for properties, this means we could also have classes declared with only some of the properties being marked with init for immutability purposes like so:

C#
class TestClass3
{
	public int TestClass3IntProperty { get; set; }
	public int TestClass3IntProperty2 { get; init; }
}

Also, because we’re talking about properties, we are also talking about generated methods with backing fields, as such, we could run custom code when setting the value for a property but only when initializing the instance like so:

C#
class TestClass4
{
	private int _testClass4IntProperty;
	public int TestClass4IntProperty
	{
		get => _testClass4IntProperty;
		init => _testClass4IntProperty = value;
	}

	private int _testClass4IntProperty2;
	public int TestClass4IntProperty2
	{
		get { return _testClass4IntProperty2; }
		init { _testClass4IntProperty2 = value; }
	}
}

So far so good, not too much to explore on this front, though the feature is very powerful for designing safer code, but let’s look at some other scenarios.

Things to Keep in Mind

Reference Properties Only Keep a Reference

Let’s say we have the following class:

C#
class TestClass5
{
	public int TestClass5IntProperty { get; set; }
}

class TestClass6
{
	public TestClass5 TestClass6RefProperty { get; init; }
}

Just like if we were to declare a field as readonly or we have a property with only a getter, this only refers to the instance held inside that field/property, as such, we can still change the non-immutable properties of instances that are referenced by an immutable field/property:

C#
void Main()
{
	TestClass6 test = new TestClass6
	{
		TestClass6RefProperty = new TestClass5
		{
			TestClass5IntProperty = 42
		}
	};

	// this is valid code because the reference to the instance 
    // of TestClass6RefProperty has not changed.
	test.TestClass6RefProperty.TestClass5IntProperty = 445;

	// this will throw an error because we're trying 
    // to change the reference of the init property
	test.TestClass6RefProperty = new TestClass5 { TestClass5IntProperty = 13 };
}

Reflection Can Still Bypass the Restriction

Let us have a look at the following code and explain why it works, we will be making use of the TestClass2 we defined before for brevity’s sake.

C#
void Main()
{
	TestClass2 test = new TestClass2
	{
		TestClass2IntProperty = 42
	};
	
	Console.WriteLine(test.TestClass2IntProperty); // will output 42
	
	PropertyInfo setPropertyInfo = test.GetType().GetProperty
                                   (nameof(TestClass2.TestClass2IntProperty));
	setPropertyInfo.SetValue(test, 512);
	
	Console.WriteLine(test.TestClass2IntProperty); // will output 512
}

If we run the code in the example above, we will see that we managed to change the value and have no compilation error, so just because the developer isn’t allowed to directly change the value of an immutable property, that doesn’t mean that they cannot.

I believe the reason this works is that many frameworks rely upon reflection to do their work. Frameworks like Entity Framework and other ORMs, serialization frameworks, and so on.

I hope you enjoyed this post and in the next one, we will be having a look at records.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)