Immutability is a quite old concept that is mostly related to the functional programming, however, it’s also (maybe not so widely) used in the object oriented programming.
An immutable variable/object can not be mutated, which means that once it’s been initialized it will never change it’s original value/reference (unless it’s deallocated).
This approach results in some great benefits such as out of the box thread safety, yet in the OOP world, it does seem to be quite often abused or even not used at all. When should we make use of the immutability? Are there some variations of this approach? How to make immutable objects in C#? Let’s find out.
Before we dive into the examples in code, here are some of the well-known benefits of having the immutable objects:
- Thread safety – value is read-only, therefore, it can be passed between different threads without any fear as it can not be changed by some other thread.
- Concurrent programming made simple – thanks to the thread safety, we get rid of locks etc. and the code gets cleaner.
- Goes hand-in-hand with the side effect free functions.
- Works great with the ValueObject in the DDD (Domain Driven Design).
Of course, it has its cons as well:
- Creating a copies of the complex objects may be difficult and/or costly.
- Is contrary to our perception of the world – most of the things in a real world are mutable.
- Does not fit well with a composite objects that should have their own lifecycle and methods that change their internal state (e.g. Entity in DDD).
The question then arises, when we should use it? I would say – use immutability everywhere where you are certain that a particular object will not have to manage its internal state. Think about simple structures such as point, address or more complex one like the option or configuration classes. Once it’s been initialized, it should never be changed, and if you really want to update some property in an already initialized object, then you should actually make a copy of it and return a new instance with the overwritten field. If you look for the immutable types in built-in the .NET platform, it turns out that there is already a plenty of them such as the DateTime, TimeSpan or string.
And what about the immutability variations that I have mentioned at the beginning? Can we define a structure that is not truly immutable? Actually yes, please take a look:
1 2 3 4 5 6 7 8 9 10 11 |
public class Point { public int X { get; protected set; } public int Y { get; protected set; } public Point(int x, int y) { X = x; Y = y; } } |
At first it looks to be an immutable object and certainly it may act as such but the issue is with the protected modifier. Nothing stops us from doing something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class Point3D : Point { public int Z { get; protected set; } public Point3D(int x, int y, int z) : base(x,y) { Z = z; CheckZeroValue(); } private void CheckZeroValue() { if (Z == 0) { X = 0; Y = 0; } } } |
Even though at a first glance it may seem ridiculous, we may be tempted to change this “immutable” object internal value as in some case it might be just simpler to implement some functionality. And it really doesn’t matter if we change the modifiers to the private type in order to prevent any derived class from updating the base properties as it’s still possible to do the same thing in the base class. Basically, we’re saying to the consumer of our “immutable” object, that it’s immutable but in some particular scenario it does not really have to be.
What can we do about it? That’s very simple, just get rid of the set modifier. Use either pure get or readonly and you will be 100% certain that you’re dealing with a truly immutable object.
It may look like this:
1 2 3 4 5 6 7 8 9 10 11 |
public class Point { public int X { get; } public int Y { get; } public Point(int x, int y) { X = x; Y = y; } } |
From now on everyone will be happy (threads too) and you’re a one stop closer to the paradigm of the functional programming. Actually in the next version of the C# which is numbered as 7, there’s a proposal for including a kind of the immutable keyword on a class level – you can read more about it here.
Last but not least, beware that usually it’s not possible to make a purely immutable structures when using e.g. the ORM, as such libraries tend to make use of the reflection mechanism and require that setter to correctly assign the values, yet you can still make these structures “immutable” by adding the protected modifier.
Before we finish, I’d like to show you one of my immutable structures in the Sentry project (comments have been removed for the sake of the code example):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class WebWatcherCheckResult : WatcherCheckResult { public Uri Uri { get; } public IHttpRequest Request { get; } public IHttpResponse Response { get; } protected WebWatcherCheckResult(WebWatcher watcher, bool isValid, string description, Uri uri, IHttpRequest request, IHttpResponse response) : base(watcher, isValid, description) { Uri = uri; Request = request; Response = response; Response = response; } public static WebWatcherCheckResult Create(WebWatcher watcher, bool isValid, Uri uri, IHttpRequest request, IHttpResponse response, string description = "") => new WebWatcherCheckResult(watcher, isValid, description, uri, request, response); } |
As you can see there is just a little of the additional overhead involved – just remove the set modifiers and provide a specialized constructor. For not so much effort you might actually gain a lot.
Pingback: .NET Developer Days sessions | Piotr Gankiewicz
We did a nice tool to generate code for creating immutable entities like that.
Take a look at
Uno.CodeGen
there: .Simple usage:
[GeneratedImmutable]
public partial class Employee
{
[EqualityHash]
public string Id { get; }
public string Name { get; }
}
* Will generate a builder to help creating instances using fluent code
* Will generate equality members (
.Equals()
&.GetHashCode()
)* Will implement
IEquatable
* Will generate
==
and!=
operator overrides* Will validate there’s no mutable, non-static members on the class
* Will generate relevant Newtonsoft’s JSON.NET serialization/deserialization when appropriate (optional)
100% Open source!