With the exception of ImmutableArray, Microsoft’s Base Class Library team has completed their production version of .NET’s Immutable Collections. Along with it are design guidelines for other types of immutable objects.

Immutable Collections are designed for situations where you want to be able to safely share collections across multiple threads, but still allow each thread to make its own changes if necessary. Unlike a read-only collection, which has to be copied in its entirety, new immutable collections can be created from existing ones relatively cheaply.

Care has to be used when working with immutable collections, for it is really easy to write “list.Add(item)” instead of “list = list.Add(item)”. Even the compiler can make this mistake, which is why immutable collections don’t have constructors. Consider this code:

list = new ImmutableList<int> {1, 2, 3};

At compile this this would become…

temp = new ImmutableList<int>();

temp.Add(1);

temp.Add(2)

temp.Add(3)

list = temp;

With the result of the three Add operations being thrown away, list has a count of 0 instead of the expected 3.

Guidelines for Immutable Objects

When creating your own immutable objects, Immo Lendwerth recommends creating WithXxx methods. For simple objects there would be one WithXxx method for each property. The method would return a copy with the object with the indicated property changed.

If the property represents a collection the pattern is slightly different. Here is an example from Immo’s announcement:

As you can see, the WithLines can accept any IEnumerable. This allows you to pass it either a new ImmutableList or the result of a LINQ expression. While this can be effective on its own, he also recommends some extra convenience methods.

ImmutableArray Pulled

The ImmutableArray class was pulled from the final release because of performance problems. In order to satisfy their memory performance goals, the ImmutableArray had to be a value type. And keeping with value type semantics, a default instance of ImmutableArray should behave like an empty array. Unfortunately the null check needed to make this happen was preventing C# from eliminating the array bounds check, an important consideration when trying to achieve good CPU performance.

Since the ImmutableArray class is very important to the Roslyn compiler project, they considered removing the null checks that were causing the performance hit. But that resulted in other problems. Immo writes,

Since all value types have an automatic default constructor that initializes the value type to its default state, empty will be an ImmutableArray<T> whose underlying array is null. Thus, the implementation of AddRange will crash with a NullReferenceException. There are other places where this problem manifests itself. Since ImmutableArray<T> implements certain collection interfaces (such as IEnumerable and IReadOnlyList), you can pass it to methods that work against the interface. Since the interface reference will be non-null, consumers won’t expect any methods or property invocations to cause a NullReferenceException.

The Base Class Library team haven’t given up on the project and are continuing to look at design options to reintroduce the ImmutableArray.