Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Array.IsReadOnly inconsistent depending on interface implementation

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.

like image 970
Greg Beech Avatar asked Nov 19 '09 18:11

Greg Beech


2 Answers

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

like image 97
Joe Avatar answered Oct 27 '22 18:10

Joe


There was plenty of agony over this decision, as evident in the comments on this feedback article.

like image 31
Hans Passant Avatar answered Oct 27 '22 17:10

Hans Passant