Making Immutable Object Graphs Easier in C#

by Jonathan Allen on May 10, 2013 |

Creating a simple immutable class in C# is easy. You just need to create a constructor and fail to create public setters. But often that’s not enough. Eventually you may want to create deep graphs that, for efficiencies sake, should be created via a builder. Or perhaps you want to make non-destructive updates by creating methods that return a copy of the object with one field changed (e.g. the AddMinutes method on DateTime). Building out all these builders and methods can be quite tedious and thus error prone.

Andrew L Arnott has a solution that relies on T4 based code generators. T4, if you aren’t familiar with it, stands for Text Template Transformation Toolkit. This is at the core of Visual Studio’s code generation capabilities and libraries such as Entity Framework rely on it. The T4 scripts take a mutable class and create an immutable clone.

Andrew’s toolkit makes a somewhat controversial decision to not emit public constructors. Instead you are expected to use a static Create method or start with the Default instance and modify it from there. This is done using the WithXxx methods, for which there is one per property.

We can make a couple more enhancements though. For classes with many properties, if you need to change several properties at once, allocating a new object with each property change as an intermediate step is wasteful and can contribute to GC pressure. So we also add a With method that takes optional parameters for every property found on the class, allowing bulk property changes. Finally, for scenarios where you have several changes to make to the object but wish to do so in a multistep fashion (or just prefer property setters to With- method calls), we can construct a Builder class that allows mutation, and then can return the immutable copy when you’re done. This pattern is very similar to String and StringBuilder in the .NET Framework, and also like the recent immutable collections mentioned earlier.

Of course if you don’t like these decisions you can easily modify the T4 templates. Andrew intentionally broke up the templates into smaller file to make this easier. You can also add your own extensions without significantly changing the base templates.

Like all well written code generators, this one heavily uses partial methods. Partial methods allow developers to inject additional logic into code-generated methods without having to modify the generated file. For example, one can implement a method to set partial default values for the immutable object. Partial methods that aren’t implemented will be automatically stripped out by the compiler, thus incurring no runtime cost.

You can read more about Arron’s experiments with Immutable Object Graphs on his blog.

Rate this Article


Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Tell us what you think

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Another possibility is to use F# 'record' types by Faisal Waris

Not sure about the entire use case here but F# has record types which seem to fit the bill quite well.

Consider two record types A and B, where B uses A, defined as follows:

type A = {Data:string}
type B = {Name:string ; A:A }

Define an instance of B (somewhat like JSON):

let b = {Name="b"; A={Data="a data"}}

Here b is immutable.

Lets say we want to 'update' the Name of b and return a new instance.

let b2 = {b with Name="b2"}

So creating a new immutable instance type B is as simple as taking an existing instance (prototype) and 'overriding' one or more fields.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

1 Discuss
General Feedback
Marketing and all content copyright © 2006-2016 C4Media Inc. hosted at Contegix, the best ISP we've ever worked with.
Privacy policy

We notice you’re using an ad blocker

We understand why you use ad blockers. However to keep InfoQ free we need your support. InfoQ will not provide your data to third parties without individual opt-in consent. We only work with advertisers relevant to our readers. Please consider whitelisting us.