26

I was going through IEnumerable and IEnumerator , but could not get one point clearly..if we have foreach, then why do we need this two interfaces? Is there any scenario where we have to use interfaces.If yes, then can somebody explain with an example. Any suggestions and remarks are welcome. Thanks.

Wondering
  • 4,950
  • 22
  • 71
  • 90

3 Answers3

60

foreach uses the interfaces in many cases. You need the interfaces if you want to implement a sequence which foreach can then use. (Iterator blocks usually make this implementation task very simple though.)

However, just occasionally it can be useful to use the iterators directly. A good example is when trying to "pair up" two different sequences. For example, suppose you receive two sequences - one of names, one of ages, and you want to print the two together. You might write:

static void PrintNamesAndAges(IEnumerable<string> names, IEnumerable<int> ages)
{
    using (IEnumerator<int> ageIterator = ages.GetEnumerator())
    {
        foreach (string name in names)
        {
            if (!ageIterator.MoveNext())
            {
                throw new ArgumentException("Not enough ages");
            }
            Console.WriteLine("{0} is {1} years old", name, ageIterator.Current);
        }
        if (ageIterator.MoveNext())
        {
            throw new ArgumentException("Not enough names");
        }

    }
}

Likewise it can be useful to use the iterator if you want to treat (say) the first item differently to the rest:

public T Max<T>(IEnumerable<T> items)
{
    Comparer<T> comparer = Comparer<T>.Default;

    using (IEnumerator<T> iterator = items.GetEnumerator())
    {
        if (!iterator.MoveNext())
        {
            throw new InvalidOperationException("No elements");
        }
        T currentMax = iterator.Current;

        // Now we've got an initial value, loop over the rest
        while (iterator.MoveNext())
        {
            T candidate = iterator.Current;
            if (comparer.Compare(candidate, currentMax) > 0)
            {
                currentMax = candidate;
            }
        }
        return currentMax;
    }
}

Now, if you're interested in the difference between IEnumerator<T> and IEnumerable<T>, you might want to think of it in database terms: think of IEnumerable<T> as a table, and IEnumerator<T> as a cursor. You can ask a table to give you a new cursor, and you can have multiple cursors over the same table at the same time.

It can take a while to really grok this difference, but just remembering that a list (or array, or whatever) doesn't have any concept of "where you are in the list" but an iterator over that list/array/whatever does have that bit of state is helpful.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks Jon for all ur help.I got it now and will study more to make it clear. Also I find following link very useful: http://stackoverflow.com/questions/558304/can-anyone-explain-ienumerable-and-ienumerator-to-me – Wondering Jul 06 '09 at 07:12
  • This is an example of where you are stuck using IEnumerator just to stay efficient. http://stackoverflow.com/questions/1018407/what-is-the-most-elegant-way-to-get-a-set-of-items-by-index-from-a-collection/1025137#1025137 – Sam Saffron Jul 06 '09 at 11:59
  • @Jon Skeet I really liked the way you coded Max method. I've one question. you used using construct with IEnumberator. I know using disposes what comes inside it (). Why did you use it on IEnumberator? – Imran Amjad May 29 '12 at 05:47
  • 1
    @ImranAmjad: `IEnumerator` implements `IDisposable`, so you're meant to call `Dispose` when you're finished with it. In particular, if the sequence you're using is generated from an iterator block, that allows `finally` blocks to be executed - so resources can be released from there. – Jon Skeet May 29 '12 at 05:48
  • Oh i see the difference. IEnumerator doesn't implement IDisposible but IEnumberator does. That's good to know. Also if we implement IEnumerator we have to implement Dispose method. As you said, if sequence is generated, it has to be disposed off. Thanks a lot! – Imran Amjad May 29 '12 at 05:55
  • @ImranAmjad: The fact that `IEnumerator` doesn't implement `IDisposable` is really just a mistake. Since C# 1.2 (in .NET 1.1) the `foreach` loop has always dispose of iterators that implement `IDisposable`. – Jon Skeet May 29 '12 at 05:58
  • First element: Select(val, index) will return the index of the current element. With a simple extension method on Enumerable, you can write: `foreach(var item in myList.SelectWithIndex()){ if (item.index == 0){Console.WriteLine(item.val.ToString());} else {...}}` – Sylvain Rodrigue Dec 05 '20 at 02:35
9

What Jon said.

  • IEnumerable or IEnumerable<T> : by implementing this an object states that it can give you an iterator that you can use to traverse over the sequence/collection/set
  • IEnumerator or IEnumerator<T> : if you call the GetEnumerator method defined in the previous interface, you get an iterator object as an IEnumerator reference. This enables you to call MoveNext() and get the Current object.
  • foreach : is a C# construct/facade in a sense in that you don't need to know how it works under the hood. It internally gets the iterator and calls the right methods for you to concentrate on what you want to do with each item (the contents of the foreach block). Most of the time, you'd just need foreach unless you're implementing your own type or custom iteration, in which case you'd need to get to know the first 2 interfaces.
Gishu
  • 134,492
  • 47
  • 225
  • 308
  • Thanks Gishu for making it more clear to me. also I find this link very useful: http://stackoverflow.com/questions/558304/can-anyone-explain-ienumerable-and-ienumerator-to-me – Wondering Jul 06 '09 at 07:13
7

Bear in mind that you don't have to implement IEnumerator and varients thereof to use foreach - IIRC, all it needs is a GetEnumerator() method that returns an object that has a MoveNext() method returning a bool, and a Current property returning an object. You don't need to use IEnumerator and IEnumerable, although it generally is a very good idea to do so. See here for more information.

Sylvain Rodrigue
  • 4,751
  • 5
  • 53
  • 67
thecoop
  • 45,220
  • 19
  • 132
  • 189
  • It's a strange old beast, that one -- you can just implement the two methods. The big problem with doing that is that the type system doesn't know the thing is enumerable, so I imagine you'd have trouble using it with, say, Linq to Objects. – Steve Cooper Jul 06 '09 at 10:10
  • 4
    Yes, it is odd. The reason we support that feature is so that you can write a collection of integers in C# 1.0 and enumerate them without boxing. Now that we have IE, there is no need for the "pattern" approach any more. – Eric Lippert Jul 06 '09 at 18:17
  • 1
    @Eric Lippert: There's another advantage to that feature: it makes it possible to avoid heap allocations for struct enumerators, though I would suggest that classes whose GetEnumerator methods return a struct should have IEnumerable.GetEnumerator methods which return classes. – supercat Mar 03 '11 at 16:28