C# Tips & Tricks: Immutable Types
This is a short introduction to immutable types, and how to create them.
First off, what is an immutable type?
The answer is pretty simple: it's a class that behaves like a built-in numeric type when passed to a method. Or in more sophisticated terms: immutable types are reference types with value type semantics.
The important difference between a value type (like int or double) and a reference type (a class or struct) is that when a value type is passed to a function, a copy of the data is made. But when passing a reference type to a function, only a copy of the reference is made, but not of the underlying data.
To illustrate this, say we have this method:
static void Func (double d)When we now call the following code:
double x = 0;The result printed to the console is '0'.
Double is a value type, and when it's passed to the method 'Func', that method recieves a copy of x. The assignment 'd = 123' inside Func assigns 123 to Func's local copy of x. But this has no effect on the world outside 'Func', and thus x is completely unaffected by the call to Func.
Now let us take a look at a reference type.
Assume we have this sample class:
class MyMutableTypeand this method:
static double BadFunc (MyMutableType t)When we now create an instance of this class and pass it to the method:
MyMutableType m = new MyMutableType(2);The console will print out '4' instead of '2', which was the value of x before the call to BadFunc.
Our object's data has changed, and we wouldn't even know it, if we didn't write BadFunc ourselves.
The reason is, that when we pass m to BadFunc, we do not create a copy of the object m, but only of the reference to the object m. Now m and the local copy t both reference the same object, and inside BadFunc, we can manipulate this object, and the changes are visible globally, as we can see when printing the value of x after the call to BadFunc. (To reach this behavior with value types we would have to pass them into a function with the 'ref' keyword).
So, now the question is: how can we prevent methods from manipulating our objects without us allowing it explicitly? And that's where immutable types come in. An immutable type is a class or struct written so that it cannot be changed once it's created.
The most prominent example of an immutable type is System.String. And string's immutability in C# is the reason you have to write code like s = s.ToUpper() instead of simply s.ToUpper(). Once a string is constructed, it cannot be changed. All the functions you can call on a string return a new string, instead of manipulating the string they are called on.
(This behavior of string might be unusual at first to someone coming from another language, but it has several advantages. The fact that strings cannot be changed after creation makes them threadsafe, allows them to be used as hashcodes without risk of corrupting the hash data structure, and is needed for .Net security reasons)
So, how can we implement our own immutable type?
Unfortunately there is no interface we can inherit from, and no attribute we can use, to make a type immutable for us. But it's still easy to do:
As i mentioned before, an immutable type cannot be changed once it is constructed, so the first step is obvious: we make all data members readonly (for those new to C# with a C/C++ background: const in C# requires one to assign a value to a variable at the same time it is declared, making its behavior more similar to #define. Readonly allows us to declare a variable without a value, and assign the value in the constructor, after which it then behaves identical to a const).
The second step is to remove all state or data changing operations inside the member functions, and instead have those functions create and return a new instance, with the changed members set in the constructor. So our class, rewritten to be immutable, will look like this:
class MyImmutableTypeNow that our class is immutable, it cannot be manipulated anymore (without us explicitly allowing it by passing it into a function with the ref keyword).
If some method tries to directly assign some value to x, a compiler error will be generated.
And take a look at out method from above, that unintentionally changed the object passed into it. It looks like this now:
static double TriesToBeBadFunc (MyImmutableType t)If we now use code like before:
MyImmutableType m = new MyImmutableType(2);The console will print out '2'.
There are no methods anymore in MyImmutableType which could change its state or data, so m cannot be manipulated through t anymore, and the assignment t = t.Square() inside the method simply assigns the new instance created by t.Square() to t, but because t is a copy of the reference m, this assignment only changes the reference t, and m stays unchanged.
To recapitulate, immutable types are classes like string, that model the behavior of the built in types, and can not be changed after creation.
To make a type immutable, you need to
- Make all fields readonly and assign them in the constructor
- And have all methods that change data or state of the object create and return a new instance instead
Andreas Hartl, 2010-2011