Enumerator


Introduction

Iterator statement is common while using list, collections, dictionaries, hash table etc and derived types. To iterate through an array or collection, in general - we need IEnumerators and IEnumerable interface. These two interfaces give flexibility while programming.

How it Works

For each statement iterates through Collection with help to IEnumerator. During runtime the iterator will check whether the Collection or custom collection have inherited IEnumerable. If it has not then a compile time error will come. The interface IEnumerable contains method GetEnumerator() which will return IEnumerator of that collection type.

IEnumerator interface contains two methods and one property

  • object Current
  • bool MoveNext()
  • void Reset()

void Reset() :

This method is used to point the enumerator just before the first element of collection. It can also be used to point to a particular element using its index number.

public void Reset()
{
//Setting the pointer to just before the beginning of collection
current=-1;
}

bool MoveNext() :

This method is to inform caller whether the next object hold any value or not. It returns true when enumerator move to next element and returns false when end of collection is reached.
public bool MoveNext()
{
//this will increment the counter variable
//and will check whether it is exceeding the actual length of our collection
return(++index<length);
}

object Current :

This property will return the current element of collection to which enumerator is pointing.

public object Current
{
get
{
//Here "array1" is the collection and "index" is the location pointer
return(array1[index]);
}
}

Way Compiler Works

List<string> name = new List<string>();
name.Add("Harvey");
name.Add("Dent");
name.Add("Clerik");

       foreach (var s in name)
            {
 
              Console.WriteLine ("Name: {0}", s);
         }


Now the above code will be manipulated by compiler as shown below:

IEnumerator<string> enumerator = name.GetEnumerator ();
while (enumerator.MoveNext())
{
string s = (string)enumerator.Current;
Console.WriteLine ("Name Compiler's Way: {0}", s);
}

Iterator

The Iterators are used because:

  • When you want to traverse a list in different ways.
  • To be able to traverse the contents of an object without exposing the internal representation.
  • To provide a uniform way of traversing different aggregate structures.
  • Iterators are also very useful in situations where the amount of data is not easily known at the start of the process.
  • Or where the data is coming from an outside, possibly asynchronous source

Yield Keyword

It can only appear inside an iterator block, which might be used as a body of a method. yield has special meaning only when it is used immediately before a return or break keyword. In another context yield can be used as identifier. It can appear in two forms

  • yield return expression;
  • yield break;

yeild type implements properties and method of IEnumerator and IDisposible. yield defined is the current position and is updated each time when MoveNext() is executed. yeild creates a state engine in IL so you can create methods that retain their state and don’t have to go through the pain of maintaining state in your code.

yield return :

This keyword is used to return items from a loop within a method and retain the state of the method through multiple calls.

The best way to think of it is that for the first call to the method, execution starts at the first line and continues until it hits a yield statement at which time it returns the value. The subsequent runs through the method continue execution at the statement after the yield, continuing to yield values or exiting at the end of the method.

The body of such methods, operators, or accessors is controlled by the following restrictions:

  • Unsafe blocks are not allowed.
  • Parameters to the method, operator, or accessor cannot be ref or out.
  • A yield statement cannot appear in an anonymous method.
  • A yield statement cannot appear in a finally block.
  • When used with expression, a yield statement cannot appear in a catch block or in a try block that has one or more catch clauses.

yeild break :

If a yield break statement is hit within a method, execution of that method stops with no return. If the yield break statement is enclosed by one or more try blocks with associated finally blocks, control is initially transferred to the finally block of the innermost try statement. When and if control reaches the end point of a finally block, control is transferred to the finally block of the next enclosing try statement. This process is repeated until the finally blocks of all enclosing try statements have been executed. Control is returned to the caller of the iterator block. This is either the MoveNext() method or Dispose method of the enumerator object. Because a yield break statement unconditionally transfers control elsewhere, the end point of a yield break statement is never reachable.

Sample Code:

delegate IEnumerable<int> D();

IEnumerator<int> GetEnumerator() {
   try {
                    yield return 1; // Ok
                     yield break; // Ok
        }
   finally {
      yield return 2; // Error, yield in finally
      yield break; // Error, yield in finally
   }
   try {
      yield return 3; // Error, yield return in try...catch
      yield break; // Ok
   }
   catch {
      yield return 4; // Error, yield return in try...catch
      yield break; // Ok
   }
   D d = delegate {
      yield return 5; // Error, yield in an anonymous method
   };
}
int MyMethod() {
   yield return 1; // Error, wrong return type for an iterator
}

Conclusion

IEnumerators and IEnumerators are used commonly in all programs sometime without even knowing they are implemented.

Whenever an error is raised by compiler with keywords IEnumerable or IEnumerator this document will help to understand the process or where it went wrong.

Up Next
    Ebook Download
    View all
    Learn
    View all