views:

569

answers:

3

This is a duplicate of: Why IEnumerable<T> inherits from IEnumerable?

IList<T> implements IList. (Edit: No it doesn't; I knew this - not sure why I wrote it.)
IEnumerable<T> implements IEnumerable.
But ICollection<T> does not implement ICollection.

What was the rationale for this and/or was it just an oversight?

+1  A: 

First, IList<T> does not implement IList either, probably for the same reasons. IList<T> implements: ICollection<T>, IEnumerable<T>, IEnumerable

Some parts of ICollection just aren't necessary, but changing an interface after it's out in the wild is breaking at best.

Look at ICollection:

public interface ICollection : IEnumerable
{
    void CopyTo(Array array, int index);

    int Count { get; }
    bool IsSynchronized { get; }
    object SyncRoot { get; }
}

It's just not properties you need in most cases, when I want a Collection I've never once needed this, nor would want to implement it. It got old would be the reasoning I suppose, but you'd have to ask the .Net team for the affirmative answer.

Nick Craver
This question refers to the original .NET 2 design - before the generic interfaces were ever released (so breakage isn't an issue). *Almost* all collection types that implement `ICollection<T>` also implement `ICollection` (with the unfortunate exception of `HashSet<T>`), and just use explicit implementation of the "awkward" members of `ICollection`.
280Z28
@280Z28 - If they were to change `ICollection` to not require those properties, it'd be breaking is what I meant...they simply chose `ICollection<T>` to not require them, simpler and non-breaking.
Nick Craver
+1  A: 

As Nick said, ICollection is pretty much useless.

These interfaces are similar only by their name, CopyTo and Count are the only properties in common. Add, Remove, Clear, Contains and IsReadOnly have been added while IsSychronized and SyncRoot have been removed.

In essence, ICollection is immutable, ICollection<T> is.

Krzysztof Cwalina has more on this topic

ICollection<T> seems like ICollection, but it’s actually a very different abstraction. We found that ICollection was not very useful. At the same time, we did not have an abstraction that represented an read/write non-indexed collection. ICollection<T> is such abstraction and you could say that ICollection does not have an exact corresponding peer in the generic world; IEnumerable<T> is the closest.

Nip
The mutability argument doesn't hold because `ICollection<T>` (mutable) implements `IEnumerable<T>` (not mutable). `ICollection<T>` generally adds to the interface members of `ICollection`, indicating it could be derived from it. The properties of `ICollection` which are not present in `ICollection<T>` could be trivially implemented by any collection, as is the case for all generic collections in the Framework except `HashSet<T>`.
280Z28
A: 

Both ICollection and ICollection<T> contain at least one method that returns the collection type (GetEnumerator) and another that takes it as an argument (CopyTo).

ICollection<T>.GetEnumerator is covariant with ICollection.GetEnumerator, because T is a more specific type than System.Object. This part is OK. This is why IEnumerable<T> is able to subclass (and thus be substitutable) for IEnumerable.

But if we want ICollection<T> to be substitutable for ICollection, we also need ICollection<T>.CopyTo to be contravariant with ICollection.CopyTo, which it is not. The ICollection<T>.CopyTo method cannot accept a parameter of a less-specific type than T (the generics grammar constrains it to be T or smaller). And if ICollection<T>.CopyTo method is not contravariant in its argument, then that means the ICollection<T> interface as a whole is not substitutable for ICollection.

That might be a little hard to make sense of; it's more complicated because it deals with arrays. It's blindingly obvious in IList<T>, where implementing both interfaces could potentially break the type safety that's supposed to be guaranteed by generics (by simply invoking the non-generic IList.Add method). But that's actually just another way of saying that the IList<T>.Add method is not contravariant in its arguments with IList.Add - the generic method cannot accept the less-specific type System.Object as an argument.

Long story short: ICollection<T> simply can't be made substitutable for ICollection under all circumstances, and therefore cannot inherit from it.

Aaronaught
The argument about variance doesn't hold up because `ICollection` is not a strongly-typed container. You could use it to say that `ICollection` can't derive from `ICollection<T>`, but you can always put a weak wrapper around a strongly typed collection, where the underlying implementation is expected to throw an `ArgumentException` when improperly used. A collection implementing `ICollection<T>` does have a `Count` property, and it can be copied element by element to an array of type `object[]` (boxing as necessary), which means it could safely implement `ICollection`.
280Z28
@280Z28: Your "weak wrapper around a strongly typed collection" isn't relevant here because generics are about **compile-time type safety**, whereas the weak wrapper has to rely on **runtime type checking**. In order for `ICollection<T>` to derive from `ICollection`, it has to be usable any place that `ICollection` is, but without contravariance, this would be invalid at compile time. Try it yourself; declare an `ICollection<T>` and use its `CopyTo` method on an `object[]` argument - it won't compile.
Aaronaught