Why Array Implements Ilist

Why array implements IList?

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.

Why System.Array class implements IList but does not provide Add()

Yes, it seems that it should have been a better design if System.Array had implemented IReadOnlyList or alike interface. However, IReadOnlyList<T> appeared in .Net 4.5 while System.Array stays from the initial .Net 1.0. Microsoft, IMHO, did their best and hid Add via explicit interface implementation

http://referencesource.microsoft.com/#mscorlib/system/array.cs,156e066ecc4ccedf

  ...
int IList.Add(Object value)
{
throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
}
...

So you can't do

int[] myArr = { 1, 2 };

myArr.Add(3);

but you can insist on using Add (and get NotSupportedException thrown) via

((IList) myArr).Add(3);

Or even

if (!myArr.IsFixedSize) {
// we have very strange array, let's try adding a value to it
((IList) myArr).Add(3);
}

How do arrays in C# partially implement IList T ?

New answer in the light of Hans's answer

Thanks to the answer given by Hans, we can see the implementation is somewhat more complicated than we might think. Both the compiler and the CLR try very hard to give the impression that an array type implements IList<T> - but array variance makes this trickier. Contrary to the answer from Hans, the array types (single-dimensional, zero-based anyway) do implement the generic collections directly, because the type of any specific array isn't System.Array - that's just the base type of the array. If you ask an array type what interfaces it supports, it includes the generic types:

foreach (var type in typeof(int[]).GetInterfaces())
{
Console.WriteLine(type);
}

Output:

System.ICloneable
System.Collections.IList
System.Collections.ICollection
System.Collections.IEnumerable
System.Collections.IStructuralComparable
System.Collections.IStructuralEquatable
System.Collections.Generic.IList`1[System.Int32]
System.Collections.Generic.ICollection`1[System.Int32]
System.Collections.Generic.IEnumerable`1[System.Int32]

For single-dimensional, zero-based arrays, as far as the language is concerned, the array really does implement IList<T> too. Section 12.1.2 of the C# specification says so. So whatever the underlying implementation does, the language has to behave as if the type of T[] implements IList<T> as with any other interface. From this perspective, the interface is implemented with some of the members being explicitly implemented (such as Count). That's the best explanation at the language level for what's going on.

Note that this only holds for single-dimensional arrays (and zero-based arrays, not that C# as a language says anything about non-zero-based arrays). T[,] doesn't implement IList<T>.

From a CLR perspective, something funkier is going on. You can't get the interface mapping for the generic interface types. For example:

typeof(int[]).GetInterfaceMap(typeof(ICollection<int>))

Gives an exception of:

Unhandled Exception: System.ArgumentException: Interface maps for generic
interfaces on arrays cannot be retrived.

So why the weirdness? Well, I believe it's really due to array covariance, which is a wart in the type system, IMO. Even though IList<T> is not covariant (and can't be safely), array covariance allows this to work:

string[] strings = { "a", "b", "c" };
IList<object> objects = strings;

... which makes it look like typeof(string[]) implements IList<object>, when it doesn't really.

The CLI spec (ECMA-335) partition 1, section 8.7.1, has this:

A signature type T is compatible-with a signature type U if and only if at least one of the following holds

...

T is a zero-based rank-1 array V[], and U is IList<W>, and V is array-element-compatible-with W.

(It doesn't actually mention ICollection<W> or IEnumerable<W> which I believe is a bug in the spec.)

For non-variance, the CLI spec goes along with the language spec directly. From section 8.9.1 of partition 1:

Additionally, a created vector with element type T, implements the interface System.Collections.Generic.IList<U>, where U := T. (§8.7)

(A vector is a single-dimensional array with a zero base.)

Now in terms of the implementation details, clearly the CLR is doing some funky mapping to keep the assignment compatibility here: when a string[] is asked for the implementation of ICollection<object>.Count, it can't handle that in quite the normal way. Does this count as explicit interface implementation? I think it's reasonable to treat it that way, as unless you ask for the interface mapping directly, it always behaves that way from a language perspective.

What about ICollection.Count?

So far I've talked about the generic interfaces, but then there's the non-generic ICollection with its Count property. This time we can get the interface mapping, and in fact the interface is implemented directly by System.Array. The documentation for the ICollection.Count property implementation in Array states that it's implemented with explicit interface implementation.

If anyone can think of a way in which this kind of explicit interface implementation is different from "normal" explicit interface implementation, I'd be happy to look into it further.

Old answer around explicit interface implementation

Despite the above, which is more complicated because of the knowledge of arrays, you can still do something with the same visible effects through explicit interface implementation.

Here's a simple standalone example:

public interface IFoo
{
void M1();
void M2();
}

public class Foo : IFoo
{
// Explicit interface implementation
void IFoo.M1() {}

// Implicit interface implementation
public void M2() {}
}

class Test
{
static void Main()
{
Foo foo = new Foo();

foo.M1(); // Compile-time failure
foo.M2(); // Fine

IFoo ifoo = foo;
ifoo.M1(); // Fine
ifoo.M2(); // Fine
}
}

How do Arrays implement IList T without implementing the property Count in C#?

This is known as an explicit interface member implementation. The interface member is not exposed as a public member of the type, but it is available by casting the reference to the interface type.

This can be done in C# like this:

interface I
{
void M();
}

class C : I
{
public int P { get; set; }
void I.M() { Console.WriteLine("M!"); }
}

Then you can use these types like this:

C obj = new C();
obj.P = 3;
((I)obj).M();

But this won't compile:

obj.M();

As JeffN825 notes, one reason for implementing the interface members explicity is that they're not supported by the type. For example, Add throws an exception (relevant discussion). Another reason for implementing a member explicity is that it duplicates another public member with a different name. That's the reason Count is implemented explicitly; the corresponding public member is Length. Finally, some members are implemented implicitly, namely, the indexer. Both of these lines work (assuming arr is an array of int):

arr[0] = 8;
((IList<int>)arr)[0] = 8;

C# and .Net why do all arrays inherit from IList?

It is often useful to be able to pass any IList to some algorithm. That algorithm might just care about being able to index into this collection. Many things fulfill this requirement: Arrays, List<T>, many custom collections.

Why don't array types have Add() method although they implement IList T ?

The explanation is in Daniel's answer in the other topic. To do what you want to achieve is to use explicit interface implementation.

    interface ILeft
{
int P { get;}
}
class Middle : ILeft
{
int ILeft.P { get { return 0; } }
}

var mid= new Middle();
var abc = mid.P // invalid;
var abc2 = (mid as ILeft).P; //valid

why does the Array class implement the Ilist Interface Explicitly not Implicitly?

It's worth noting to start with that you don't have to implement an entire interface implicitly or explicitly - it's a member-by-member decision... and I there are different reasons for different members. I'm only guessing (very few people can give a definitive answer here) but:

  • Count: I suspect that the Length property has special support when you're dealing with a specific array type (I haven't checked the IL) and is more efficient; it's cleaner not to present both to developers
  • IsFixedSize: If you know you're dealing with an array, you know the size is fixed
  • IsReadOnly: If you know you're dealing with an array, you know it's mutable
  • IsSynchronized: If you know you're dealing with an array, you know it's not synchronized
  • Item: The non-generic IList interface would expose the indexers which accept/return object; the specific type of array indexers are more type-safe (and again, probably supported more directly). The accessor methods in Array provide options for arrays with a rank != 1.
  • SyncRoot: There's never a SyncRoot for an array
  • Add, Insert, Remove, RemoveAt, Clear: You can never change an array's size, so none of these are appropriate

In other words, if you already have the compile-time information that this is an array, you already either know the answer or definitely can't use these operations - or have a better way of doing it.

The ones which could be reasonable:

  • Contains, CopyTo, IndexOf: These could all be exposed via implicit interface implementation. I don't know why they're not

GetEnumerator (from IEnumerable) is already exposed via implicit interface implementation.

Why ArrayList implement IList, ICollection, IEnumerable?

With the following code:

interface I1 { }
interface I2 : I1 { }

class Foo: I2 { }

If you look at Foo through reflection you will find

class Foo: I2, I1 { }

Which is also valid to compile and gives the same result.

So the difference is moot, and in documenting Foo you might as well write it with both interfaces.

Also see the SO question: Why collections classes in C# (like ArrayList) inherit from multiple interfaces if one of these interfaces inherits from the remaining?

Why List implements IList

Take a look at this blog post by Eric Lippert: So many interfaces. He has some great insights, as always

Make sure to read the whole thing, but here's the quote that answers the question:

Why then does List implement IList?

It is a bit odd, since List for any
type other than object does not
fulfill the full contract of IList.
It's probably to make it easier on
people who are updating old C# 1.0
code to use generics; those people
were probably already ensuring that
only the right types got into their
lists. And most of the time when
you're passing an IList around, it is
so the callee can get by-index access
to the list, not so that it can add
new items of arbitrary type.



Related Topics



Leave a reply



Submit