A typed array implements both the System.Collections.IList
and System.Collections.Generic.ICollection<T>
interfaces, which both have their own IsReadOnly
properties. But what on earth is going on here?
var array = new int[10];
Console.WriteLine(array.IsReadOnly); // prints "False"
var list = (System.Collections.IList)array;
Console.WriteLine(list.IsReadOnly); // prints "False"
var collection = (System.Collections.Generic.ICollection<int>)array;
Console.WriteLine(collection.IsReadOnly); // prints "True"
The IList
view of the array behaves as I'd expect, returning the same as the array itself, however the ICollection<T>
view of the array returns true.
Is there any rational explanation for this behaviour, or is it a compiler/CLR bug? (I'd be really surprised if it's the latter as you'd imagine this would have been found before now, but it's so counter-intuitive I can't think what the explanation could be...).
I'm using C#3.0/.NET 3.5 SP1.
From MSDN:
IList is a descendant of the ICollection interface and is the base interface of all non-generic lists. IList implementations fall into three categories: read-only, fixed-size, and variable-size. A read-only IList cannot be modified. A fixed-size IList does not allow the addition or removal of elements, but it allows the modification of existing elements. A variable-size IList allows the addition, removal, and modification of elements.
The ICollection<T> interface does not have an indexer, so a fixed-size ICollection<T> is automatically readonly - there is no way to modify an existing item.
Possibly ICollection<T>.IsFixedSize would have been a better property name than ICollection<T>.IsReadOnly, but both imply the same thing - impossible to add or remove elements, i.e. the same as IList.IsFixedSize.
An array is a fixed-size list, but is not readonly as elements can be modified.
As an ICollection<T>, it is readonly, since an ICollection<T> has no way to modify elements.
This may appear confusing, but it is consistent and logical.
What is slightly inconsistent is that the generic IList<T> interface has an IsReadOnly property inherited from ICollection<T> whose semantics are therefore different from the non-generic IList.IsReadOnly. I imagine the designers were aware of this inconsistency but were unable to go back and change the semantics of the non-generic IList for backwards compatibility reasons.
To summarize, an IList can be:
Variable-size.
IList.IsFixedSize = false
IList.IsReadOnly = false
ICollection<T>.IsReadOnly = false
Fixed-size (but elements can be modified, e.g. an Array)
IList.IsFixedSize = true
IList.IsReadOnly = false
ICollection<T>.IsReadOnly = true
Read-only (elements can not be modified)
IList.IsFixedSize = true
IList.IsReadOnly = true
ICollection<T>.IsReadOnly = true
There was plenty of agony over this decision, as evident in the comments on this feedback article.
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