Why does IEnumerable<T> inherits from IEnumerable

In the same vein as Krzysztof’s post on the reason why IEnumerator extends IDisposable, I thought I’d post this thoughtful response from Anders Hejlsberg on why IEnumerable<T> inherits from IEnumerable.

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. For example, it would be convenient if an IList<T> could be passed to code that expects an IList.

 

As it turns out, the only generic interface for which this is possible is IEnumerable<T>, because only IEnumerable<T> is contra-variant: 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. (As an aside, they would have been contra-variant if T was used only in input positions, but that doesn't really matter here.)

 

The effect of having a non-contra-variant generic interface inherit from its non-generic counterpart is best illustrated with an example. Imagine IList<T> inherited from IList. IList<T> would now have two Add methods--its own Add(T) method and an inherited Add(object) method. While this is technically possible, the inherited Add(object) method would completely defeat the purpose of IList<T> because it can be called with an object of any type. In other words, I would be able to write:

 

IList<int> numbers = GetIntList(...);

numbers.Add("hello");

 

and the call to Add would invoke IList<int>'s implementation of Add(object), which presumably would throw an exception at run-time. The strong typing provided by IList<T> would be lost and there would be little reason to have IList<T> in the first place. For this reason, IList<T> doesn't inherit from IList. Of course, we recommend that collections implement both, but now the loss of type safety is called out because I have to explicitly obtain the IList implementation:

 

IList<int> numbers = GetIntList(...);

IList objects = (IList)numbers;

objects.Add("hello");

 

The story is different for IEnumerable<T>. It has one method, GetEnumerator(), which returns an IEnumerator<T>, which in turn has a Current property of type T. In all cases, T occurs in an output position, and strong typing is not defeated if methods returning object are added. Intuitively, it is "safe" to treat a T as an object, but not vice versa.

 

So, to answer your question, IEnumerable<T> inherits from IEnumerable because it can! :-)