40

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.

Timwi
  • 65,159
  • 33
  • 165
  • 230
Morgan Cheng
  • 73,950
  • 66
  • 171
  • 230

4 Answers4

52

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 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.)

<...snip...>

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

Marc L.
  • 3,296
  • 1
  • 32
  • 42
Mark Brackett
  • 84,552
  • 17
  • 108
  • 152
  • 1
    That's interesting... I was really annoyed that `IList` didn't inherit `IList`, but as soon as you take variance into account it starts to make sense... – Thomas Levesque Nov 29 '09 at 01:01
  • 1
    It's too bad Microsoft never defined IReadableByIndex and IReadableByIndex(Of Out T), which could have been inherited by IList and IList(Of T). IReadableByIndex could be inherited by IReadableByIndex(Of T) without difficulty, and the variance of read-only indexable collections would have been very nice. – supercat Jan 16 '11 at 23:06
  • @supercat Microsoft may have heard your prayers, because since you wrote that comment, they've introduced [`IReadOnlyList`](http://msdn.microsoft.com/en-us/library/hh192385.aspx) where the `out` signifies covariance in `T`. – Jeppe Stig Nielsen Jan 28 '13 at 15:56
  • @JeppeStigNielsen: They did indeed add that interface, though unfortunately there's no mechanism for `IList` implementations to automatically implement it, nor is there any means by which code can determine whether an object that implements `IReadOnlyList` offers is a mutable list, a read-only view (that can't be cast to be mutable) to a list that might be changed externally, a view of a list that's never supposed to change, or a list that can be guaranteed never to change. – supercat Jan 28 '13 at 16:03
  • 1
    What bugs me is the `IEnumerator IEnumerable.GetEnumerator()` method i have to implement. It does not have accessor, VS mark is as private & no matter what I tried it just won't get called. Even doing `IEnumerator iter = intlist.GetEnumerator();` calls the generic public method. Will something bad happen if I just leave `throw new NotImplementedException();` in it? – Dave_cz Feb 27 '16 at 22:16
  • @Dave_cz - you can call it by casting to the non-generic IEnumerable interface ((IEnumerable)intList).GetEnumerator(). I suspect (but don't know, and haven't tested) that's the GetEnumerator call that a foreach call will use, so NotImplementedException would probably be a Bad Idea. – Mark Brackett Feb 27 '16 at 23:29
  • `((IEnumerable)intList).GetEnumerator()` does use the non-generic version, altough it's usefulness is debatable. Foreach uses the generic version, that is first thing I tested. Moreover why the non-generic method do not have public accessor like basically all interface implementations? – Dave_cz Mar 02 '16 at 22:34
  • In case it confused anyone else, the quote in the answer does confuse contra-variance with covariance. IEnumerable is covariant, not contra-variant. – drifter Sep 23 '17 at 03:55
19

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.

Marc L.
  • 3,296
  • 1
  • 32
  • 42
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
3

It's for backward compatibility. If you call a .Net 1.1 function that expects a vanilla IEnumerable you can pass in your generic IEnumerable.

Luckilly the generic IEnumerator inherits from the old-style IEnumerator

I usually implement a private method that returns an enumerator and then pass it for both the old and new style GetEnumerator method.

    private IEnumerator<string> Enumerator() {
        // ...
    }

    public IEnumerator<string> GetEnumerator() {
        return Enumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
        return Enumerator();
    }
Mendelt
  • 36,795
  • 6
  • 74
  • 97
  • When implementing IEnumerable, you're required to also implement the private IEnumerable.GetEnumerator() method. Fortunately, the code above does well, so you're not duplicating effort. – spoulson Oct 21 '08 at 13:10
  • 2
    @spoulson: Not private. It's part of an interface, so it must be implemented publicly. Mendelt's example doesn't implement it privately, it implements it explicitly, so that it is only available when the object is cast to IEnumerable. – Joel B Fant Oct 21 '08 at 14:06
  • @spoulson: The second two methods were generated when I clicked implement interface in visual studio, didn't really look at it. You can just add a using for System.Collections and make the third function explicitly public. – Mendelt Oct 22 '08 at 08:13
2

This is so that it will work with classes that do not support generics. Additionally, .NET generics don't let you do things like cast IList<long> as IList<int>, so non generic versions of interfaces can be quite useful when you need a fixed base class or interface.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
jezell
  • 2,532
  • 14
  • 12