As I’m sure you’re aware, the word immutable means “unchanging.” So, C# immutable types obviously refers to a C# type that can’t change.
What would be the attraction of such a thing?
That’s what this post will cover.
Immutable is an Idea that Takes Getting Used To
In our post about value objects, we briefly covered the topic of immutability. There, we talked about the more conceptual side of immutability, as in “value objects must be immutable types since it wouldn’t make sense for them to be otherwise.”
As it turns out, there are many practical benefits to making your types immutable—not just the value objects. I remember the first time I came across the concept of immutable types. I was researching string immutability, and somehow Google led me to a Stack Overflow question about immutability in general.
Up until that moment, I wasn’t familiar with the concept of immutability.
I must admit it wasn’t an easy idea to wrap my head around. I remember thinking, “Why would I want objects that don’t change? Things change in real life!”
So, I’m writing this post with my past self in mind. The goal here is twofold: to explain why immutable types are desirable and to provide a quick recipe on how to work with them.
Download the NDepend trial for free and get instant feedback about many rules, including ones about immutability.
Immutable Types: What Are They?
Put simply, immutable types (objects or data structures) are types that, once initialized, cannot change their internal state. That’s it.
Of course, there can be much more to it than that. I’ll get back to that before the post is over.
But for now, I think this definition is enough for us.
A classic example of an immutable type in .NET is the System.DateTime. Once you have a DateTime, you can never change it. When you use methods such as “AddDays,” you get a new DateTime, which is the result of the operation.
The Benefits of Immutable Types
There’s no shortage of posts out there singing immutability’s praises. Now that we’ve briefly defined what immutable types are, the next question is “Why are they A Good Thing™“?
They’re Easier To Reason About
A piece of code is said to be easy to reason about if it’s predictable: if it fulfills the promises it makes to its clients, or in other words, if it adheres to the principle of least astonishment.
And in what way do immutable types show this quality?
Simple. Since they never change, they can never be accidentally altered in a way that would lead to undesirable effects.
For a more concrete example, let’s do a quick thought exercise using an already mentioned type: System.DateTime.
Imagine the newest version of .NET comes with a new type called “MutableDateTime.” It’s essentially the same as a regular DateTime except for two things.
- It’s a class instead of a struct. (In other words, it’s a reference type instead of a value type.)
- It’s mutable, meaning methods such as “AddDays” change the object in place instead of returning a new instance.
Now, you’ll have to pardon me for invoking the ultimate programming blog post cliché, but I’m going to do it. I’m going to use a “Person” class as an example. Consider the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class Person { private readonly string name; public string Name => name; private readonly MutableDateTime dateOfBirth; public MutableDateTime DateOfBirth => dateOfBirth; public Person(string name, MutableDateTime dateOfBirth) { this.name = name; this.dateOfBirth = dateOfBirth; } } |
Now, I retrieve a person instance from the database:
1 |
var person = personRepository.FindById(42); |
And for whatever reason, I need to know the date of the day 10 days after the date of birth. So, I do this:
1 2 |
var dateOfBirth = person.DateOfBirth; var tenDaysLater = dateOfBirth.AddDays(10); |
Now, considering that the date of birth of the person retrieved from the database was, let’s say, December 7, 1989, what would the following line print?
1 |
Console.WriteLine("Date of birth: " + person.DateOfBirth); |
If you answered “December 17,” you’re right. That may sound like a silly example, but something extremely similar to this caused a lot of problems in a certain mainstream language that I won’t name.
Code like this can cause extremely nasty bugs. But that can all be avoided if the types are created immutable.
Thread-Safety
This is, essentially, a consequence of the previous benefit.
If immutable types can’t be changed at all, they can’t be changed by another thread, which makes them safe to be shared. When some piece of code needs to change the value, it won’t actually change anything; it will create a new instance with the updated values.
No Need For Defensive Copying
This is also a consequence of the first point. Since immutable types will never change, you don’t need to be afraid of them being changed accidentally. Thus, you don’t have to come up with strategies to prevent that from happening, such as defensive copying/cloning.
Immutable Types Are Always in a Valid State
I could’ve also named this subtopic “they guarantee their invariants at all time.” The idea here is very simple. If you combine immutability with object validation at creation time, then you’re guaranteed to have a valid object for that point on. Here’s a quick example:
1 2 |
var zipCode = new ZipCode(candidateText); // this will throw if "candidateText" is null, or blank/white-space, or not a valid zip code in whatever way. Console.WriteLine(zipCode); // if it reaches that line and the ZipCode class is immutable, then you're guaranteed to always have a valid zip code. |
C# Immutable Types: an Implementation Recipe
All this talk can leave you with the impression that creating an immutable is something of a sophisticated task. I’d say that, yes, it is a sophisticated approach. But that doesn’t mean it’s hard to do.
Consider the code of the following “RGBColor” class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class RGBColor { public int Red { get; set; } public int Green { get; set; } public int Blue { get; set; } public RGBColor(int red = 0, int green = 0, int blue = 0) { Red = red; Green = green; Blue = blue; } } |
In real life, such a class would probably have overrides for “ToString,” “Equals,” “GetHashCode,” and the like, and probably some more methods relevant to its domain. I didn’t include any of this out of simplicity. In real life, you’d also probably use byte instead of int, but I’ve chosen int for a specific reason, and I’ll get back to that.
So what about this design?
Well, this class is not only mutable—it’s embarrassingly mutable. It has automatic properties for both reading and writing and optional parameters that enable the caller to omit the values for any of the component colors.
So, how do we turn this into an immutable type?
First step: make the setters private.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class RGBColor { public int Red { get; private set; } public int Green { get; private set; } public int Blue { get; private set; } public RGBColor(int red = 0, int green = 0, int blue = 0) { Red = red; Green = green; Blue = blue; } } |
Now the object can’t be changed from the outside.
It can still be changed from inside the class, though. So let’s get rid of the setters for good and implement readonly backing fields. While we’re at it, let’s change the constructor to make it use the fields and turn the optional parameters into required ones:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class RGBColor { private readonly int red; private readonly int green; private readonly int blue; public int Red => red; public int Green => green; public int Blue => blue; public RGBColor(int red, int green, int blue) { this.red = red; this.green = green; this.blue = blue; } } |
And that’s pretty much it. We can summarize what we did in three simple steps:
- Remove setters.
- Turn the private fields into readonly (or create them, if they don’t exist at this point).
- Change the constructor so it requires all needed parameters at construction time.
Easy, isn’t it?
Readonly Automatically Implemented Properties
But this code still makes me a little bit uneasy. We had to get fields back only to enable the setting of the properties. They feel like noise to me, and I wish there was a way to achieve the same result without needing them.
Well, as it turns out, there is such a way.
Starting with C# 6.0, there is this feature called “readonly automatically implemented properties” which, at least for me, is a very self-explanatory name. (Even though, technically, the correct name should be “write-once” instead of readonly. But this is just me nitpicking).
Here is what the code looks like, after employing this feature:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class RGBColor { public int Red { get; } public int Green { get; } public int Blue { get; } public RGBColor(int red, int green, int blue) { Red = red; Green = green; Blue = blue; } } |
There’s one more thing we need to do, though, and this has to do with validation. We must ensure that only values in the range of 0 to 255 are allowed.
The current design allows you to pass any arbitrary integers as parameters, and we don’t want that. So it’d probably make sense to add guard clauses to verify that the parameters fall into the correct range and throw otherwise. I’ll leave that as an exercise for the reader.
Why Isn’t Everything Immutable?
If immutable types are so awesome, then why isn’t everything immutable? That’s actually a very good question, and it’s one that’s been asked quite a lot before.
One of the most often-cited pitfalls of immutable types is performance. The overhead generated by creating new objects might have an impact on the performance of your application.
Sometimes, mutability is just the most intuitive mental model for an application. Think of GUI programming or games.
In the end, many (maybe most) developers just use mutable types because they’re the default in the mainstream OOP languages.
The Only Constant Is Change…Unless We’re Talking About Your Types
The goal of this post was to introduce you to the basics of immutability. There’s definitely a lot more to immutable types than this. Eric Lippert, for instance, has written several great articles about the subject.
The use of immutable types offers some very nice benefits, but it’s no silver bullet. Everything in software development is a tradeoff, and it’s part of your job as a developer to weigh the pros and cons of different approaches and figure out what makes more sense for your project and team.
That being said, I do suggest you to give immutability a serious attempt. Maybe you can adopt an “immutable-first approach”: everything is immutable by default until there’s a very good reason to change.
And of course, don’t forget to research how you can take advantage of NDepend to enforce immutability in your projects.
Here’s a more succinct example of the final RGBColor implementation! Properties that use
{ get; }
are readonly by default.public class RGBColor
{
public int Red { get; }
public int Green { get; }
public int Blue { get;}
public RGBColor(int red, int green, int blue)
{
Red = red;
Green = green;
Blue = blue;
}
}
What is the benefit of having backing fields for read-only properties? In other words, why not doing this:
public int Red {get;}
public int Green {get;}
public int Blue {get;}
public RGBColor(int red, int green, int blue)
{
this.Red = red;
this.Green = green;
this.Blue = blue;
}
Nice.
Don’t write “put simply” – it and other similar phrases are verbiage. Just leave them out.
To improve your writing – ie to make it far more concise – I recommend “Politics and The English Language” by George Orwell. Writing Prose, and On Writing Well.
cheers.
“The Benefits of Immutable Types”: totally agree!
“C# Immutable Types: an Implementation Recipe”: totally agree with the other comments that you should use C# 6’s read-only auto properties
public int Red { get; }
“Why Isn’t Everything Immutable?”: True, and I’d add, never pre-optimize, and always pinpoint the real bottleneck with a performance profiler before sacrificing maintainability for performance. Also “Sometimes, mutability is just the most intuitive mental model for an application” — this is arguable, but true for us who have not been doing FP for some time. So I’d say, continue striving for more immutability (and more functional programming in general). This adds more benefits of less error-proneness through state, easier testability, and more readable, maintainable code (provided you use good naming, as with any code).
Also see https://github.com/kofifus/With for an easy way to add a
With
method that constructs a new ‘mutation’ of the object with a changed member specified by a lambda expression. so you can do for example:var rgbColor1 = rgbColor.With(c => c.Red, 9);
Hello – I’m trying to determine why NDepend (2018.2.1) says that static classes are not Immutable. For example, this class violates rule ND1909:TypesTaggedWithImmutableAttributeMustBeImmutable.
[Immutable]
public static class ImmutableArrayExtensions
{
public static ImmutableArray ToIdArray(this ImmutableArray idAndNames)
{
return idAndNames.Select(x => x.Id).ToImmutableArray();
}
}
The person’s DateOfBirth won’t change in the first example of this article. DateTime in .NET is immutable, ironically, so the AddDate method doesn’t modify the existing DateTime instance, but instead returns a new instance.