Remarks for IList.IsReadOnly
state the following:
A collection that is read-only does not allow the addition, removal, or modification of elements after the collection is created.
Does this mean that a custom class implementing IList cannot add or remove elements internally or does it just forbid users from doing it using the interface mothods?
If internal modifications are allowed, does that mean that code that expects an IList
with IsReadOnly
true to never change is inherently broken?
If internal modifications are not allowed, does that mean that it is impossible to write a valid IList
which can change internally, but not allow users to modify it?
https://msdn.microsoft.com/en-us/library/cc645181%28v=vs.110%29.aspx
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.
For another type, there's nothing to stop you exposing a truly immutable collection as IList<T>
with that property returning true, but you don't have to.
We can also see that cases from the framework library that return true for IsReadOnly
often allow the inner collection to be mutated.
List<int> initialList = new List<int> { 1 };
IList<int> readOnly = new ReadOnlyCollection<int>(initialList);
Console.WriteLine(readOnly.Count); // 1
Console.WriteLine(readOnly.IsReadOnly); // True
initialList.Add(2);
Console.WriteLine(readOnly.Count); // 2
Really, IsReadOnly
tells you that the mutating methods like Add
and the setter side of the indexer will fail, not that the object is immutable through all means.
An interesting consideration in this regard: Some places within the framework libraries themselves need read-only lists that are indeed truly read-only. Their public interface returns either ReadOnlyCollection<T>
or IReadOnlyList<T>
(e.g. BlockExpression.Expressions
returns ReadOnlyCollection<T>
) but they don't trust read-only collections passed to them. They use an internal type called TrueReadOnlyCollection<T>
which is created as a wrapper of a fresh array, copied on construction so nothing else can change it. That type is trusted to never change and so can be shared between uses, but all other cases aren't.
Internal code can indeed change it.
The IsReadOnly
indicates that the list cannot be directly changed via that interface.
I agree that "code that expects an IList
with IsReadOnly
true to never change is inherently broken".
Microsoft has introduced the ImmutableList<T>
class, and perhaps this is one of the reasons.
IReadOnlyList
to the rescue? Er, unfortunately no:
List<int> test = new List<int> {1};
IReadOnlyList<int> readOnly = test; // Because List<T> implements IReadOnlyList<int>.
Console.WriteLine(readOnly[0]); // Prints 1.
test[0] = 2;
Console.WriteLine(readOnly[0]); // Prints 2. Oops.
It's for these reasons that some time ago I proposed that we should not declare interfaces for immutable types.
And here's another example using List<T>.AsReadOnly()
. This demonstrates that you can mutate a list with IReadOnly
set true by using only built-in .Net types:
Console.WriteLine(shouldBeReadOnly.IsReadOnly); // Readonly? Yep! This prints True.
Console.WriteLine(shouldBeReadOnly[0]); // Prints 1.
Console.WriteLine(shouldBeReadOnly.Count); // Prints 1.
test[0] = 2;
test.Add(-1);
Console.WriteLine(shouldBeReadOnly[0]); // Prints 2. Oops. It wasn't readonly at all.
Console.WriteLine(shouldBeReadOnly.Count); // Prints 2.
Note that you can change the number of elements as well as the elements themselves.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With