97

.NET 4.5 has a new namespace System.Collections.Immutable

This package provides collections that are thread safe and guaranteed to never change their contents, also known as immutable collections.

I'm confused. Isn't the thread safety problem already solved by the ReadOnlyCollection class? Why use ImmutableList instead?


I know there's also an IReadOnlyList interface. That doesn't solve the thread safety problem implicitly, because other threads may edit the object by another interface.

Colonel Panic
  • 132,665
  • 89
  • 401
  • 465
  • 1
    This post seems to explain it fairly well " http://blog.slaks.net/2013-06-11/readonly-vs-immutable/ " – Damian May 11 '15 at 10:56
  • 2
    Just for infomration `System.Collections.Immutable` has to be installed through nuget package manager. I had to do so in a project targeting .Net framework 4.6.2. – RBT Feb 17 '17 at 00:33

6 Answers6

96

With a ReadOnlyCollection:

A collection that is read-only is simply a collection with a wrapper that prevents modifying the collection; therefore, if changes are made to the underlying collection, the read-only collection reflects those changes.

This can't happen with an ImmutableList.

James Thorpe
  • 31,411
  • 5
  • 72
  • 93
  • 8
    I would suggest the most important feature of immutable collections is how they are manipulated by creating new immutable collections and the underlying efficient "copy-on-write" mechanisms. Therefore the other two answers seem to be more relevant to me. – koloman Aug 16 '16 at 06:28
  • 1
    Immutability is a big lie at least in C# .Net. I'm not sure about other programming languages. In C# even with `ImmutableList` anyone can go via unmanaged route and change the contents of that memory location without CLR's knowledge. But yes as long as all the parties abide by the legalities offered by .Net platform we can safely assume `ImmutableList` is immutable indeed. – RBT Feb 22 '17 at 23:27
  • 39
    @RBT it's the same for any program really - if you've got code running inside the same process, the OS won't stop you changing anything – James Thorpe Feb 23 '17 at 06:26
  • Even Immutable collection can be modified by modifying underlying collection. I am unable to understand why we say it can't be done? – Ashwini Mohan Jul 29 '21 at 17:18
  • @AshwiniMohan If you use the proper public API of `ImmutableList`, there is no underlying collection to modify - when you call `Add` etc, it returns a new copy of the collection with the additions - the original is left untouched. With `ReadOnlyCollection`, while you can't modify it through that class itself, you can still modify the underlying collection that `ReadOnlyCollection` was initialised with. If you start using reflection or other methods of modifying data, then yes - anything goes, as per my previous comment. – James Thorpe Jul 30 '21 at 08:15
  • Right, that's what my understanding is. Definitely thread safety is an advantage with immutable list but at last, everything is modifiable. In ReadOnly list, it just takes only single foreach loop, and for immutable list, it takes another nested foreach to modify. Maybe I was over expecting. But thanks @JamesThorpe for the explanation. Appreciate it. – Ashwini Mohan Jul 31 '21 at 12:27
38

ReadOnlyCollection<T> doesn't solve any of the thread safety problems. It is merely a wrapper around Ilist<T>. It doesn't exposes members to modify the collection, but you can always modify it with the underlying collection reference.

If the underlying collection is modified, it isn't safe to enumerate the ReadOnlyCollection<T>. If you do, you'll get the same InvalidOperationException with message "Collection was modified; enumeration operation may not execute...".

From ReadOnlyCollection<T>

A ReadOnlyCollection can support multiple readers concurrently, as long as the collection is not modified. Even so, enumerating through a collection is intrinsically not a thread-safe procedure. To guarantee thread safety during enumeration, you can lock the collection during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.

ImmutableList on the other hand is immutable and thus inherently thread safe.

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
30

ReadOnlyCollection, as the name suggests, can only be read.

On the other hand, you can append/remove items to/from an ImmutableList by calling its Add/Remove/Clear methods, for example, which return a new immutable list.

dcastro
  • 66,540
  • 21
  • 145
  • 155
  • 24
    I'll add that this makes the `ImmutableList<>` very similar to a `string`. – xanatos May 11 '15 at 11:18
  • @xanatos indeed, or to any (properly implemented) value type. – dcastro May 11 '15 at 11:20
  • 5
    With ImmutableList, isn't the important distinction, just like string, that you're not modifying the original. You're creating a new object with the change (say with Remove) but the original version remains unchanged? – Simon Vane Oct 12 '18 at 16:51
9

In multi-threaded scenarios, be advised that read-only collections are still not thread-safe.

From the ReadOnlyCollection<T> documentation:

... if changes are made to the underlying collection, the read-only collection reflects those changes

Since collections, like List<T> and others, are not thread safe, so is not the read-only collection.

Important: There are some corner cases which you won't find explicitly explained in MSDN. Some of the operations that seemingly only read content of a collection, are in fact modifying the internal structures of the collection. Why is this not specified? - One obvious reason is because that is an implementation detail which doesn't reflect on the API. The result is that even if you don't modify the List<T> wrapped into an ReadOnlyCollection<T>, and only use getters, the crash could still happen in multi-threaded environment!

Bottom line is that common collections, even when wrapped into a ReadOnlyCollection cannot be used in multi-threaded environment out of the box.

As opposed to ReadOnlyCollection, immutable collections do guarantee that none of the internal structures will ever change after a reference to a collection has been obtained. Note that these structures are still not truly immutable. They are, instead, freezable. That means that the structure will internally change for a while until it is frozen and returned to the caller. Beyond that point, all other calls on the immutable collection will only make modifications outside the structures accessible through the original reference.

Conclusion: Read-only collections are not thread-safe; immutable collections are thread-safe.

Zoran Horvat
  • 10,924
  • 3
  • 31
  • 43
3

So far all technical aspects was told but no code. I created a small example shows main difference.

Consider both readonly collection and immutable list pointing same object in this case "strings list".

If we add a new item to "strings list" after readonly collection creation , we can see that readonly collection is modified.(So we managed to find a way to mutate readonly collection)

However,when we take same steps on immutable lists,immutable list stays intact

List<string> strings = new List<string>()
{
    "Visual Studio 2017","Visual Studio 2019"
};
ReadOnlyCollection<string> readOnlyStrings = strings.AsReadOnly();
var immutableList = strings.ToImmutableList();
strings.Add("Visual Studio 2022");
Console.WriteLine("Readonly List");

foreach (string item in readOnlyStrings)
{
    Console.WriteLine(item);
}
Console.WriteLine("Immutable List");

foreach (string item in immutableList)
{
    Console.WriteLine(item);
}

Screenshot

erhan355
  • 806
  • 11
  • 23
1

The main benefit is memory efficiency, not thread-safety.

The immutable collections in System.Collections.Immutable are designed in a way that allows modified versions of the collection to share data structures with un-modified copies of the same collection. The differences are encoded in clever ways that don't require duplication of the whole data structure. There is nice explanation of this at Immutable Collections.

ReadOnlyCollection requires a completely seperate copy of the collection to be created whenever a modification is made. This uses more memory and puts a higher load on the garbage collector.

As you've observed, both classes seek to guarantee that the collection can't have observable modifications. And both classes achieve that to some extent, assuming they aren't misused.

andypea
  • 1,343
  • 11
  • 22
  • *"`ReadOnlyCollection` requires a completely separate copy of the collection to be created whenever a modification is made."* -- Why? Why create a copy, and not just modify the collection? The owner of the collection can always modify it. It's only the users of the read-only facade that can't mutate the collection. – Theodor Zoulias Aug 03 '22 at 00:59
  • Having a single owner who is able to mutate the collection and multiple read-only users who never create modified versions is certainly a common situation. However, in some other situations the read-only users will want to take the old collection and create a new collection that adds, removes or modifies an element. This second use case is the one where immutable collections may be more efficient. – andypea Aug 03 '22 at 02:11