.NET Immutability Tip #3: Protect your properties AND your methods.
A common immutability practice is to simply protect the property setter with an immutability flag. Take a simple class that has a single integer field for to back our property and a single boolean flag to mark it immutable.
using System;
public class SimplyImmutable {
private int field1 = 0;
private bool immutable = false;
public SimplyImmutable() {
}
public int Field1 {
get { return field1; }
set {
if ( !immutable ) {
field1 = value;
}
}
}
}
As soon as the immutable flag is set, you can no longer set the field value. This is nice. Time to add a few methods. First we need a MakeImmutable method. This will set our flag. Normally, our application will set this before giving any other code access to the class. Second, we'll add an IncrementField method, but we'll make a mistake, so see if you catch it.
public void MakeImmutable() {
immutable = true;
}
public void IncrementField() {
field1++;
}
See the mistake yet? We accessed field1 directly, and didn't use the immutability flag. This problem goes away if you use the property instead of the private field or if you use the immutability flag. In this small example the problem is easy to spot. However, in a larger class with several properties, many backing fields, and maybe 10 or more methods, you can fail to find issues early in the game. The gem here is to use the property inside of our methods so that we don't have to write a bunch of extra code in each of them to handle immutability.
public void IncrementField() {
Field1++;
}
What happens if you are setting the Field1 property multiple times? You start to incur the wrath of the flag check on each set. If this happens, then go ahead and move your immutability check into your method and then convert over to using the private field again. Note if you do this process by first using the property and then only later converting over to use the private field and the immutability flag, you can do it on an as needed basis and ensure that you are protecting yourself.
public void IncrementField() {
if ( !immutable ) { // Work with field multiple times, if not then just use the property instead }
}
This tip has many natural extensions into other gotchas that I'll cover later. Think for a bit about making our fields protected so they can be accessed from derived classes, and how that might introduce resource access issues. I'll be examining this abstraction in the next tip.