56

In C#, what is the key difference (in terms of features or use cases) between these two containers? There doesn't appear to be any information comparing these on Google.

System.Collections.ObjectModel.ReadOnlyDictionary System.Collections.Immutable.ImmutableDictionary

I understand that an ImmutableDictionary is thread-safe. Is the same true of a ReadOnlyDictionary?

This is not a duplicate of How to properly use IReadOnlyDictionary?. That question is about how to use IReadOnlyDictionary. This question is about the difference between the two (which, as someone commented on that thread back in 2015, would be a different question - ie. this one)

matt_rule
  • 1,220
  • 1
  • 12
  • 23
  • 3
    You can only read from a `ReadOnlyDictionary`. The `ImmutableDictionary` allows you to do stuff like add and clear, but instead of mutating the dictionary it creates a new one with the changes. – juharr Oct 24 '17 at 13:00
  • What juharr said. And to expand on that, a readonly collection will always be thread-safe specifically because you can't update the values, nor add to it. – Dan Rayson Oct 24 '17 at 13:01
  • 1
    Absolutely wrong ! Just because the consumer of the dictionary can't update it that does not mean it is thread safe. The owner of the underlying dictionary can update it, and in that case, it is not Thread safe at all. ([MSDN](https://msdn.microsoft.com/en-us/library/gg712875(v=vs.110).aspx): Any instance members are not guaranteed to be thread safe.) – fharreau Oct 24 '17 at 13:28
  • Does this answer your question? [How to properly use IReadOnlyDictionary?](https://stackoverflow.com/questions/32560619/how-to-properly-use-ireadonlydictionary) – Jim G. Dec 02 '21 at 14:02
  • No, sorry it does not answer this question. That question is about how to use IReadOnlyDictionary. This question is about the difference between the two (which, as someone commented in that thread back in 2015, would be a different question - ie. this one) – matt_rule Dec 02 '21 at 15:53

5 Answers5

74
  • A ReadOnlyDictionary can be initialized once via constructor, then you can't add or remove items from it (they throw NotSupportedExceptions). It's useful if you want to ensure that it won't be modified while it's sent across multiple layers of your application.
  • An ImmutableDictionary has methods to modify it like Add or Remove, but they will create a new dictionary and return that, the original one remains unchanged and the copy of the new immutable dictionary is returned.

Note that:

  • You initialize the ReadOnlyDictionary by passing another dictionary instance to the constructor. That explains why a ReadOnlyDictionary is mutable (if the underlying dictionary is modified). It's just a wrapper that is protected from direct changes.
  • You can't use a constructor for ImmutableDictionary: How can I create a new instance of ImmutableDictionary?

That also explains why the ReadOnlyDictionary is not thread-safe (better: it's as thread-safe as the underlying dictionary). The ImmutableDictionary is thread-safe because you can't modify the original instance (neither directly nor indirectly). All methods that "modify" it actually return a new instance.

But if you need a thread-safe dictionary and it's not necessary that it's immutable, use a ConcurrentDictionary instead.

Yennefer
  • 5,704
  • 7
  • 31
  • 44
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
10

A ReadOnlyDictionary<TKey,TValue> is a wrapper around another existing IDictionary<TKey,TValue> implementing object.

Importantly, whilst "you" (the code with access to the ReadOnlyDictionary) cannot make any changes to the dictionary via the wrapper, this does not mean that other code is unable to modify the underlying dictionary.

So unlike what other answers may suggest, you cannot assume that the ReadOnlyDictionary isn't subject to modification - just that "you" aren't allowed to. So for example, you cannot be sure that two attempts to access a particular key will produce the same result.

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
2

In addition to the current answers, I would add that ImmutableDictionary is slower and usually will use more memory.

  • Why slower? Behind the scenes, the ImmutableDictionary isn't a hash table. It uses an AVL tree which is a self-balancing tree, and therefore, its access complexity is O(logn). On the other hand, the other dictionaries use a hash table behind the scenes and the access complexity for them is O(1).
  • Why more memory allocation? Every time the dictionary is being changed it creates a new dictionary because it is immutable.
Misha Zaslavsky
  • 8,414
  • 11
  • 70
  • 116
  • 2
    *"Every time the dictionary is being changed it creates a new dictionary because it is immutable."* -- Actually what happens is that a new node is created, and connected to the nodes of the existing binary tree. Maybe some more nodes are created to balance the tree. It's not that the tree is recreated from scratch after each `Add` or `Remove`. Most of the existing structure is preserved. – Theodor Zoulias Sep 16 '22 at 20:21
0

Instead of describing what these two classes do, it would be better to describe what it actually means for something to be read-only or immutable, as there is a key distinction which doesn't really give much option to those two implementations.

Read-only is part of an "interface" of a class, its set of public methods and properties. Being read-only means that there is no possible sequence of actions an external consumer of the class could do in order to affect its visible state. Compare with a read-only file for example; no application can write to such a file using the same API that made it possible to make it read-only in the first place.

Does read-only imply thread-safe? Not necessarily – a read-only class could still employ things like caching or optimization of its internal data structures and those may be (poorly) implemented in a way that breaks when invoked concurrently.

Does read-only imply never-changing? Also no; look at the system clock, for example. You cannot really affect it (with the default permissions), you can only read it (making it read-only by definition), but its value changes based on the time.

Never-changing means immutable. It is a much stronger concept, and, like thread-safety, is a part of the contract of the whole class. The class must actively ensure that no part of its instance ever changes during its lifetime, with respect to what can be observed externally.

Strings are immutable in .NET: as long as the integrity of the runtime is not compromised (by memory hacking), a particular instance of a string will never be different from its initially observed value. Read-only files are, on the other hand, not much immutable, as one could always turn read-only off and change the file.

Immutable also does not imply thread-safe, as such an object could still employ techniques that modify its internal state and are not thread-safe (but it's generally easier to ensure).

The question whether immutable implies read-only depends on how you look at it. You can usually "mutate" an immutable object in a way that doesn't affect external code that may be using it, thus exposing an immutable object is at least as strong as exposing a read-only one. Taking a substring of a string is like deleting a part of it, but in a safe manner.


This brings us back to the original question about the two classes. All ReadOnlyDictionary has to do is to be read-only. You still have to provide the data in some way, with an internally wrapped dictionary, and you and only you can still write to it through the internal dictionary. The wrapper provides "strong" read-only access (compared to a "weak" read-only access that you get just by casting to IReadOnlyDictionary). It is also thread-safe, but only when the underlying dictionary is thread-safe as well.

ImmutableDictionary can do much more with the strong guarantee that the data it holds cannot be changed. Essentially you can "patch" parts of it with new data and obtain a modified "copy" of the structure but without actually copying the complete object. It is also thread-safe by the virtue of its implementation. Similarly to a StringBuilder, you use a builder to make changes to an instance and then bake them to make the final instance of an immutable dictionary.

IS4
  • 11,945
  • 2
  • 47
  • 86
-2

ReadOnlyDictionary: is ReadOnly, cannot add or remove

ImmutableDictonary: can add or remove but it is immutable like string. There is new object to added and removed.

Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
sonertbnc
  • 1,580
  • 16
  • 26