Why Does (Does It Really) List<T> Implement All These Interfaces, Not Just Ilist<T>

Why does (does it really?) ListT implement all these interfaces, not just IListT?

UPDATE: This question was the basis of my blog entry for Monday April 4th 2011. Thanks for the great question.

Let me break it down into many smaller questions.

Does List<T> really implement all those interfaces?

Yes.

Why?

Because when an interface (say, IList<T>) inherits from an interface (say IEnumerable<T>) then implementers of the more derived interface are required to also implement the less derived interface. That's what interface inheritance means; if you fulfill the contract of the more derived type then you are required to also fulfill the contract of the less derived type.

So a class is required to implement all the methods of all the interfaces in the transitive closure of its base interfaces?

Exactly.

Is a class that implements a more-derived interface also required to state in its base type list that it is implementing all of those less-derived interfaces?

No.

Is the class required to NOT state it?

No.

So it's optional whether the less-derived implemented interfaces are stated in the base type list?

Yes.

Always?

Almost always:

interface I1 {}
interface I2 : I1 {}
interface I3 : I2 {}

It is optional whether I3 states that it inherits from I1.

class B : I3 {}

Implementers of I3 are required to implement I2 and I1, but they are not required to state explicitly that they are doing so. It's optional.

class D : B {}

Derived classes are not required to re-state that they implement an interface from their base class, but are permitted to do so. (This case is special; see below for more details.)

class C<T> where T : I3
{
public virtual void M<U>() where U : I3 {}
}

Type arguments corresponding to T and U are required to implement I2 and I1, but it is optional for the constraints on T or U to state that.

It is always optional to re-state any base interface in a partial class:

partial class E : I3 {}
partial class E {}

The second half of E is permitted to state that it implements I3 or I2 or I1, but not required to do so.

OK, I get it; it's optional. Why would anyone unnecessarily state a base interface?

Perhaps because they believe that doing so makes the code easier to understand and more self-documenting.

Or, perhaps the developer wrote the code as

interface I1 {}
interface I2 {}
interface I3 : I1, I2 {}

and the realized, oh, wait a minute, I2 should inherit from I1. Why should making that edit then require the developer to go back and change the declaration of I3 to not contain explicit mention of I1? I see no reason to force developers to remove redundant information.

Aside from being easier to read and understand, is there any technical difference between stating an interface explicitly in the base type list and leaving it unstated but implied?

Usually no, but there can be a subtle difference in one case. Suppose you have a derived class D whose base class B has implemented some interfaces. D automatically implements those interfaces via B. If you re-state the interfaces in D's base class list then the C# compiler will do an interface re-implementation. The details are a bit subtle; if you are interested in how this works then I recommend a careful reading of section 13.4.6 of the C# 4 specification.

Does the List<T> source code actually state all those interfaces?

No. The actual source code says

public class List<T> : IList<T>, System.Collections.IList

Why does MSDN have the full interface list but the real source code does not?

Because MSDN is documentation; it's supposed to give you as much information as you might want. It is much more clear for the documentation to be complete all in one place than to make you search through ten different pages to find out what the full interface set is.

Why does Reflector show the whole list?

Reflector only has metadata to work from. Since putting in the full list is optional, Reflector has no idea whether the original source code contains the full list or not. It is better to err on the side of more information. Again, Reflector is attempting to help you by showing you more information rather than hiding information you might need.

BONUS QUESTION: Why does IEnumerable<T> inherit from IEnumerable but IList<T> does not inherit from IList?

A sequence of integers can be treated as a sequence of objects, by boxing every integer as it comes out of the sequence. But a read-write list of integers cannot be treated as a read-write list of objects, because you can put a string into a read-write list of objects. An IList<T> is not required to fulfill the whole contract of IList, so it does not inherit from it.

Why does ListT implement so many interfaces?

Indeed List<T> would have just implemented like this

public class List<T> : IList<T>, IList

It is the reflector or such decompiler shows you all the interfaces in inheritance.

Try this

public class List2<T> : IList<T>

I just compiled this and viewed in reflector, which shows like this

public class List2<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable

Why does ListT implement IListT, ICollectionT and IEnumerableT?

Yes, it makes no difference in this case. In some cases it can make a difference, if you're using a base class which already implements an interface but you wish to reimplement it yourself explicitly - but in this case there's no base class (other than the implicit object) and it would have behaved exactly the same way.

Contrary to my recollections, I don't believe there's a difference in the way the class is represented in metadata whether the code explicitly declares all the interfaces or not. Here's an example:

interface IFoo {}
interface IBar : IFoo {}

class FooBar1 : IBar {}
class FooBar2 : IBar, IFoo {}

Both ildasm and Reflector show the same information for FooBar1 and FooBar2... it shows both of them implementing IBar and IFoo.

In other words, we can't tell whether the original source code for List<T> actually specifies all the interfaces or not. Maybe it does, maybe it doesn't - but it doesn't matter either way.

EDIT: For completeness, I also checked the cases where you're extending two interfaces with another interface. I can't find a difference in the metadata in that case, either. I'm sure I remember some situation in which it was evident, but I can't find it now.

Why it is not possible to use IListT in interface definition and ListT in inplementation?

It has to be an exact match between the interface's method and the concrete method that implements the interface's method. One option is explicit interface implementation, which allows you to satisfy the interface while keeping your more-specific public API on the type. And usually just proxy the methods, so there is no code duplication:

public interface ICategory
{
IList<Product> Products { get; }
}

public class Category : ICategory
{
IList<Product> ICategory.Products { get { return Products ; } }
public List<Product> Products { get { ...actual implementation... } }
}

ListT or IListT

If you are exposing your class through a library that others will use, you generally want to expose it via interfaces rather than concrete implementations. This will help if you decide to change the implementation of your class later to use a different concrete class. In that case the users of your library won't need to update their code since the interface doesn't change.

If you are just using it internally, you may not care so much, and using List<T> may be ok.

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.

Why doesn't `IListT` inherit from `IReadOnlyListT`?

@w.b put a link to New interfaces IReadOnlyList and IReadOnlyDictionary in the comments that contains an answer:

Why did we not change the existing interfaces to extend the read-only interfaces?

It looks like a reasonable assumption that it works because the read-only interfaces are purely a subset of the read-write interfaces. Unfortunately, it is incompatible because at the metadata level every method on every interface has its own slot (which makes explicit interface implementations work).


Immo Landwerth | .NET Framework Team (BCL) | http://blogs.msdn.com/b/bclteam/

To explain this a bit more clearly:

Suppose that a program written for .NET 4.0 contains a class MyList<T> that implements IList<T>. It clearly cannot implement IReadOnlyList<T> as that interface doesn't exist.

Now suppose the system administrator installs .NET 4.5 and suppose that .NET 4.5 made IList<T> implement IReadOnlyList<T>.

If the program would then be loaded, the runtime would detect that MyList<T> claims to implement IList<T>, but doesn't actually implement all the methods: it doesn't implement IReadOnlyList<T>'s methods. The program would no longer work.

The C# compiler might be able to match the methods by name, but the runtime doesn't do this. Since .NET 4.5 was supposed to have backwards binary compatibility, interfaces couldn't be extended to implement other interfaces, not even if those other interfaces contain a strict subset of the required methods.

Why generic IList does not inherit non-generic IList

As you note, T in IList<T> is not covariant. As a rule of thumb: any class that can modify its state cannot be covariant. The reason is that such classes often have methods that have T as the type of one of their parameters, e.g. void Add(T element). And covariant type parameters are not allowed in input positions.

Generics were added, among other reasons, to provide type safety. For example, you can't add an Elephant to a list of Apple. If ICollection<T> were to extend ICollection, then you could call ((ICollection)myApples).Add(someElephant) without a compile-time error, as ICollection has a method void Add(object obj), which seemingly allows you to add any object to the list, while in practice you can only add objects of T. Therefore, ICollection<T> does not extend ICollection and IList<T> does not extend IList.

Anders Hejlsberg, one of the creators of C#, explains it like this:

Ideally all of the generic collection interfaces (e.g. ICollection<T>, IList<T>) would inherit from their non-generic counterparts such that generic interface instances could be used both with generic and non-generic code.

As it turns out, the only generic interface for which this is possible is IEnumerable<T>, because only IEnumerable<T> is contra-variant [sic1]: In IEnumerable<T>, the type parameter T is used only in "output" positions (return values) and not in "input" positions (parameters). ICollection<T> and IList<T> use T in both input and output positions, and those interfaces are therefore invariant.

1) IEnumerable<T> is co-variant


Since .Net 4.5 there are the IReadOnlyCollection<out T> and IReadOnlyList<out T> covariant interfaces. But IList<T>, ICollection<T> and many of the list and collection classes don't implement or extend them. Frankly, I find them not very useful, as they only define Count and this[int index].


If I could redesign .Net 4.5 from the ground up, I would have split the list interface into a read-only covariant interface IList<out T> that includes Contains and IndexOf, and a mutable invariant interface IMutableList<T>. Then you could cast IList<Apple> to IList<object>. I implemented this here:

M42 Collections - Covariant collections, lists and arrays.

Why does IListT not provide all the methods that ListT does? Which should I use?

Personally I would use IList<T> rather than List<T>, but then use LINQ (Select, Where etc) instead of the List-specific methods.

Casting to List<T> removes much of the point of using IList<T> in the first place - and actually makes it more dangerous, as the implementation may be something other than List<T> at execution time.

Why does IListT implement IEnumerableT and ICollectionT while ICollectionT itself implements IEnumerableT

Interface "inheritance" is one of the most misleading terms in software engineering. You didn't inherit squat, interfaces don't have any implementation so you can't inherit it either. You only inherit the demand to implement the methods.

Adding to that demand by repeating the interface declaration doesn't change anything, you already had the demand and adding an extra demand makes no difference whatsoever. So since it doesn't matter, Microsoft helpfully just repeats the interface so you can tell in one fell swoop what interfaces are implemented by, say, List. You don't have to drill down to the interface declaration to see that List implements IEnumerable as well. It is a self-documenting coding style, recommended.

Do beware of the other side of this medal, two distinct interfaces with the exact same method can be implemented with just a single method implementation. While that is often useful, sometimes that is not what you want. Say, ICowboy and IPainter, they both have a Draw() method. It should not do the same thing :) You then have to fall back to an explicit implementation to avoid the ambiguity.

Addressing the Resharper complaint, it isn't very helpful of course. Resharper tends to assume the worst from a programmer. But if you want to shut it up then you need to remove IOne from the IThree inheritance list, it is redundant. Same thing for the class that implements IThree, you'd also need to remove ITwo and IOne from the inheritance list. Or just turn off the warning.



Related Topics



Leave a reply



Submit