C# Programming Guide
Using Iterators (C# Programming Guide)

The most common way to create an iterator is to implement the GetEnumerator method on the IEnumerable interface, for example:

C#
public System.Collections.IEnumerator GetEnumerator()
{
    for (int i = 0; i < 10; i++)
    {
        yield return i;
    }
}

The presence of the GetEnumerator method makes the type an enumerable type and allows using the foreach statement. If the method above was part of a class definition for ListClass, then it would be possible to use foreach on the class like this:

C#
static void Main()
{
    ListClass listClass1 = new ListClass();

    foreach (int i in listClass1)
    {
        System.Console.WriteLine(i);
    }
}

The foreach statement invokes ListClass.GetEnumerator() and uses the returned enumerator to iterate through the values. For an example of how to create a generic iterator that returns a IEnumerator<(Of <(T>)>) interface, see How to: Create an Iterator Block for a Generic List (C# Programming Guide).

It is also possible to use named iterators to support different ways of iterating through the same collection of data. For example, you could provide one iterator which returns elements in ascending order, and one which returns elements in descending order. An iterator can also have parameters to enable clients to control all or part of the iteration behavior. The following iterator implements the IEnumerable interface using the named iterator SampleIterator:

C#
// Implementing the enumerable pattern
public System.Collections.IEnumerable SampleIterator(int start, int end)
{
    for (int i = start; i <= end; i++)
    {
        yield return i;
    }
}

The named iterator is invoked like this:

C#
ListClass test = new ListClass();
foreach (int n in test.SampleIterator(1, 10))
{
    System.Console.WriteLine(n);
}

You can use more than one yield statement in the same iterator as in the following example:

C#
public System.Collections.IEnumerator GetEnumerator()
{
    yield return "With an iterator, ";
    yield return "more than one ";
    yield return "value can be returned";
    yield return ".";
}

You can then print the results using the following foreach statement:

C#
foreach (string element in new TestClass())
{
    System.Console.Write(element);
}

This example displays the following text:

With an iterator, more than one value can be returned.

On each successive iteration of the foreach loop (or the direct call to IEnumerator.MoveNext), the next iterator code body resumes after the previous yield statement and continues to the next until the end of the iterator body is reached, or a yield break statement is encountered.

Iterators do not support the IEnumeratorReset()()() method. To re-iterate from the beginning, you must obtain a new iterator.

See Also

Tasks

Concepts

Reference

Tags :


Community Content

Fafach
Argument checking in iterators: Consider using Guarded Iterators

Code in iterators gets executed only when calling the iterator's MoveNext() method, not upon calling the iterator function itself. (That call just sets up an IEnumerable object that contains the iterator code.)

Stuff that should happen immediately should therefore not go into an iterator method. A nice workaround is to pack the argument checking into a simple method, and call the generator from that simple method.

Guarded Iterator pattern:

public IEnumerable<T> Iterator (string argument)
{
  // immediate argument checking
  if (argument == null)
throw new ArgumentNullException ("argument");
  
  // delayed iterator execution
  return IteratorImplementation (argument);
}
  
private IEnumerable<T> IteratorImplementation (string argument)
{
yield return ...
}

Here's a simple example: Providing the lines of a stream as IEnumerable<string>

Naive implementation: Everything goes into the iterator method, argument checking is therefore delayed.

public static class StreamReaderExtensions
{
public static IEnumerable<string> Lines (this StreamReader reader)
{
if (reader == null)
throw new ArgumentNullException ("reader");
  
for (;;)
{
string line = reader.ReadLine ();
if (line == null)
yield break;
yield return line;
}
  }
}

Fixed implementation: Argument checking in simple method up-front.

public static class StreamReaderExtensions
{
  // this method is executed immediately when called
public static IEnumerable<string> Lines (this StreamReader reader)
{
if (reader == null)
throw new ArgumentNullException ("reader");
return LinesCore (reader);
}
  
  // this iterator is executed partially whenever MoveNext is called on the generated enumerator
private static IEnumerable<string> LinesCore (StreamReader reader)
{
for (;;)
{
string line = reader.ReadLine ();
if (line == null)
yield break;
yield return line;
}
  }
}

Here's how these methods work differently in a more complicated caller scenario:

StreamReader stream = null;
var numbers = from line in stream.Lines () // Fixed implementation throws ArgumentNullException here, when calling Lines()
              select int.Parse (line); 
foreach (var number in numbers) // Naive implementation throws ArgumentNullException here, when calling MoveNext()
Console.WriteLine(number * 2);

Thanks to Eric Lippert for pointing out the problem and providing the solution: http://blogs.msdn.com/ericlippert/archive/2008/09/08/high-maintenance.aspx (he makes another point there too, I simplified the example to concentrate on the exception problem.)

I believe this is a pattern, or at least an idiom, that should be considered whenever writing an iterator that does some argument checking. It deserves a name too, I like Guarded Iterator.


Page view tracker