Why array implements IList?

C#ArraysIlistLiskov Substitution-Principle

C# Problem Overview


See the definition of System.Array class

public abstract class Array : IList, ...

Theoretically, I should be able to write this bit and be happy

int[] list = new int[] {};
IList iList = (IList)list;

I also should be able to call any method from the iList

 ilist.Add(1); //exception here

My question is not why I get an exception, but rather why Array implements IList?

C# Solutions


Solution 1 - C#

Because an array allows fast access by index, and IList/IList<T> are the only collection interfaces that support this. So perhaps your real question is "Why is there no interface for constant collections with indexers?" And to that I have no answer.

There are no readonly interfaces for collections either. And I'm missing those even more than a constant sized with indexers interface.

IMO there should be several more (generic) collection interfaces depending on the features of a collection. And the names should have been different too, List for something with an indexer is really stupid IMO.

  • Just Enumeration IEnumerable<T>
  • Readonly but no indexer (.Count, .Contains,...)
  • Resizable but no indexer, i.e. set like (Add, Remove,...) current ICollection<T>
  • Readonly with indexer (indexer, indexof,...)
  • Constant size with indexer (indexer with a setter)
  • Variable size with indexer (Insert,...) current IList<T>

I think the current collection interfaces are bad design. But since they have properties telling you which methods are valid (and this is part of the contract of these methods), it doesn't break the substitution principle.

Solution 2 - C#

The remarks section of the documentation for IList says:

> 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.

Obviously, arrays fall into the fixed-size category, so by the definition of the interface it makes sense.

Solution 3 - C#

Because not all ILists are mutable (see IList.IsFixedSize and IList.IsReadOnly), and arrays certainly behave like fixed-size lists.

If your question is really "why does it implement a non-generic interface", then the answer is that these were around before generics came along.

Solution 4 - C#

It's a legacy that we have from the times when it wasn't clear how to deal with read only collections and whether or not Array is read only. There are IsFixedSize and IsReadOnly flags in the IList interface. IsReadOnly flag means that collection can't be changed at all and IsFixedSize means that collection does allow modification, but not adding or removal of items.

At the time of .Net 4.5 it was clear that some "intermediate" interfaces are required to work with read only collections, so IReadOnlyCollection<T> and IReadOnlyList<T> were introduced.

Here is a great blog post describing the details: Read only collections in .NET

Solution 5 - C#

Definition of IList interface is "Represents a non-generic collection of objects that can be individually accessed by index.". Array completely satisfies this definition, so must implement the interface. Exception when calling Add() method is "System.NotSupportedException: Collection was of a fixed size" and occurred because array can not increase its capacity dynamically. Its capacity is defined during creation of array object.

Solution 6 - C#

Having an array implement IList (and transitively, ICollection) simplified the Linq2Objects engine, since casting the IEnumerable to IList/ICollection would also work for arrays.

For example, a Count() ends up calling the Array.Length under-the-hood, since it's casted to ICollection and the array's implementation returns Length.

Without this, the Linq2Objects engine would not have special treatment for arrays and perform horribly, or they'd need to double the code adding special-case treatment for arrays (like they do for IList). They must've opted to make array implement IList instead.

That's my take on "Why".

Solution 7 - C#

Also implementation details LINQ Last checks for IList , if it did not implement list they would need either 2 checks slowing down all Last calls or have Last on an Array taking O(N)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionoleksiiView Question on Stackoverflow
Solution 1 - C#CodesInChaosView Answer on Stackoverflow
Solution 2 - C#Brian RasmussenView Answer on Stackoverflow
Solution 3 - C#user541686View Answer on Stackoverflow
Solution 4 - C#VladimirView Answer on Stackoverflow
Solution 5 - C#meirView Answer on Stackoverflow
Solution 6 - C#Herman SchoenfeldView Answer on Stackoverflow
Solution 7 - C#user1496062View Answer on Stackoverflow