The next preview (preview 6) will bring a new type ReadOnlySet<T>
. This is a read-only set that is similar to ReadOnlyCollection<T>
. Let's see how it works and why it was introduced.
So what does that new try to solve? Well, there are two issues with the "current" approach. Current as in .NET 8
1. IReadOnlySet
isn't enough
For sure, we already have the IReadOnlySet<T>
interface. So why isn't that enough? Let's have a look at List<T>
and IReadOnlyList<T>
, which suffers somewhat from the same issue.
var list = new List<int> { 1, 2, 3 };
IReadOnlyList<int> readOnlyList = list;
// You can easily cast it back to a List<T> and modify it
var list2 = (List<int>)readOnlyList;
list2.Add(4);
Of course, consumers of your API shouldn't do that, but you can't prevent it. The same applies to IReadOnlySet<T>
. So that is why we have a AsReadOnly
on List
:
var list = new List<int> { 1, 2, 3 };
var readOnlyList = list.AsReadOnly();
// You can't cast it back to a List<T>
var list2 = (List<int>)readOnlyList; // Exception
That is exactly what ReadOnlySet<T>
will do for IReadOnlySet<T>
:
var set = new HashSet<int> { 1, 2, 3 };
var readonlySet = new ReadOnlySet<int>(set);
2. Wrong workaround
If users tried to roll out their own way of creating a read-only set, they would have to do something like ImmutableHashSet<T>
or FrozenSet
.
var set = new HashSet<int> { 1, 2, 3 };
var readOnly = set.ToFrozenSet(); // Or ToImmutableHashSet()
While technically this is working, it has two major problems:
- Immutable collections like
ImmutableHashSet<T>
orFrozenSet
need to copy the whole collection into its own memory to guarantee immutability. This can be a waste of memory and CPU cycles. - It's not really readonly. Readonly and immutable are two different concepts. Readonly means you can't modify it, but you can still modify the original collection, which then would be reflected in the "readonly" collection. With immutable collections, this wouldn't be reflected. I even have a whole blog post about it: "ReadOnlyCollection is not an immutable collection".
And that is why we have ReadOnlySet<T>
now. If you want to dive deeper, check out the API Propsal: https://github.com/dotnet/runtime/issues/100113