24

I was wondering if there are any times where it's advantageous to use an IEnumerator over a foreach loop for iterating through a collection? For example, is there any time where it would be better to use either of the following code samples over the other?

IEnumerator<MyClass> classesEnum = myClasses.GetEnumerator();
while(classesEnum.MoveNext())
    Console.WriteLine(classesEnum.Current);

instead of

foreach (var class in myClasses)
    Console.WriteLine(class);
lomaxx
  • 113,627
  • 57
  • 144
  • 179

4 Answers4

31

First, note that one big difference in your example (between foreach and GetEnumerator) is that foreach guarantees to call Dispose() on the iterator if the iterator is IDisposable. This is important for many iterators (which might be consuming an external data feed, for example).

Actually, there are cases where foreach isn't as helpful as we'd like.

First, there is the "first item" case discussed here (foreach / detecting first iteration).

But more; if you try writing the missing Zip method for stitching two enumerable sequences together (or the SequenceEqual method), you find that you can' use foreach on both sequences, since that would perform a cross-join. You need to use the iterator directly for one of them:

static IEnumerable<T> Zip<T>(this IEnumerable<T> left,
    IEnumerable<T> right)
{
    using (var iter = right.GetEnumerator())
    {
        // consume everything in the first sequence
        foreach (var item in left)
        {
            yield return item;

            // and add an item from the second sequnce each time (if we can)
            if (iter.MoveNext())
            {
                yield return iter.Current;
            }
        }
        // any remaining items in the second sequence
        while (iter.MoveNext())
        {
            yield return iter.Current;
        }                
    }            
}

static bool SequenceEqual<T>(this IEnumerable<T> left,
    IEnumerable<T> right)
{
    var comparer = EqualityComparer<T>.Default;

    using (var iter = right.GetEnumerator())
    {
        foreach (var item in left)
        {
            if (!iter.MoveNext()) return false; // first is longer
            if (!comparer.Equals(item, iter.Current))
                return false; // item different
        }
        if (iter.MoveNext()) return false; // second is longer
    }
    return true; // same length, all equal            
}
Community
  • 1
  • 1
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
19

According to the C# language spec:

A foreach statement of the form

foreach (V v in x) embedded-statement

is then expanded to:

{
    E e = ((C)(x)).GetEnumerator();
    try {
            V v;
            while (e.MoveNext()) {
                    v = (V)(T)e.Current;
                    embedded-statement
            }
    }
    finally {
            ... // Dispose e
    }
}

The essense of your two examples are the same, but there are some important differences, most notably the try/finally.

Jay Bazuzi
  • 45,157
  • 15
  • 111
  • 168
  • 1
    Interesting because this also shows what kind of implicit conversions (optionally User-defined) can be triggered – sehe Nov 08 '12 at 15:17
12

In C#, foreach is doing exactly the same thing as the IEnumerator code that you posted above. The foreach construct is provided to make it easier for the programmer. I believe that the IL generated in both cases is the same/similar.

Jay Bazuzi
  • 45,157
  • 15
  • 111
  • 168
Andy
  • 30,088
  • 6
  • 78
  • 89
4

I used it sometimes when the first iteration do something different from others.

For example, if you want to print members to console and separate results by line, you can write.

using (IEnumerator<MyClass> classesEnum = myClasses.GetEnumerator()) {
    if (classEnum.MoveNext())
        Console.WriteLine(classesEnum.Current);
    while (classesEnum.MoveNext()) {
        Console.WriteLine("-----------------");
        Console.WriteLine(classesEnum.Current);
    }
}

Then the result is

myClass 1
-----------------
myClass 2
-----------------
myClass 3

And another situation is when I iterate 2 enumerators together.

using (IEnumerator<MyClass> classesEnum = myClasses.GetEnumerator()) {
    using (IEnumerator<MyClass> classesEnum2 = myClasses2.GetEnumerator()) {
        while (classesEnum.MoveNext() && classEnum2.MoveNext())
            Console.WriteLine("{0}, {1}", classesEnum.Current, classesEnum2.Current);
    }
}

result

myClass 1, myClass A
myClass 2, myClass B
myClass 3, myClass C

Using enumerator allows you to do more flexible on your iteration. But in most case, you can use foreach

  • First iteration: You can use foreach( var item in myList.Select(val, index)) to get the index of the current element. With an extension method, you can even write foreach(var item in myList.SelectWithIndex()) – Sylvain Rodrigue Dec 05 '20 at 02:25