This might be a old question: Why does IEnumerable<T>
inherit from IEnumerable
?
This is how .NET do, but it brings a little trouble. Every time I write a class implements IEumerable<T>
, I have to write two GetEnumerator()
functions, one for IEnumerable<T>
and the other for IEnumerable
.
And, IList<T>
doesn't inherit from IList.
I don't know why IEnumerable<T>
is designed in other way.
ICollection inherits from IEnumerable. You therefore have all members from the IEnumerable interface implemented in all classes that implement the ICollection interface.
IEnumerable is an interface defining a single method GetEnumerator() that returns an IEnumerator interface. It is the base interface for all non-generic collections that can be enumerated. This works for read-only access to a collection that implements that IEnumerable can be used with a foreach statement.
IEnumerable in C# is an interface that defines one method, GetEnumerator which returns an IEnumerator interface. This allows readonly access to a collection then a collection that implements IEnumerable can be used with a for-each statement.
You can create a class that implements the IEnumerable<T> interface to expose source data as enumerable data. Your class that implements the IEnumerable(T) interface will require another class that implements the IEnumerator<T> interface to iterate through the source data.
Straight from the horse's mouth (Hejlsberg):
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 anIList<T>
could be passed to code that expects anIList
.
As it turns out, the only generic interface for which this is possible is
IEnumerable<T>
, because onlyIEnumerable<T>
is contra-variant: InIEnumerable<T>
, the type parameter T is used only in "output" positions (return values) and not in "input" positions (parameters).ICollection<T>
andIList<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.)
<...snip...>
So, to answer your question, IEnumerable<T>
inherits from IEnumerable
because it can! :-)
The answer for IEnumerable
is: "because it can without affecting type safety".
IEnumerable
is a "readonly" interface - so it doesn't matter that the generic form is more specific than the nongeneric form. You don't break anything by implementing both. IEnumerator.Current
returns object
, whereas IEnumerator<T>.Current
returns T
- that's okay, as you can always legitimately convert to object
, although it may mean boxing.
Compare this with IList<T>
and IList
- you can call Add(object)
on an IList
, whereas that may well be invalid for any particular IList<T>
(anything other than IList<object>
in fact).
Brad Abram's blogged with Anders' answer about this very question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With