Lijin John Lijin John - 1 month ago 8
C# Question

How is the GetEnumerator method get invoked in a foreach loop in C#?

I was going through Iterator pattern and got a little confused when i stumpled upon logic i found in the sample code

class MonthCollection : IEnumerable
{

public string[] months = {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"};

public IEnumerator GetEnumerator()
{
// Generates values from the collection
foreach (string element in months)
yield return element;
}
}

static void Main()
{

MonthCollection collection = new MonthCollection();
// Consumes values generated from collection's GetEnumerator method
foreach (string n in collection)
Console.Write(n + " ");
Console.WriteLine("\n");
}


in this code, the normal way to access the Array to strings would be to call the months variable like this in the foreach statement

MonthCollection collection = new MonthCollection();

foreach (string n in collection.months){ }



but i could not understand how is the foreach getting access to the
collection without even calling the months variable directly


MonthCollection collection = new MonthCollection();

foreach (string n in collection){}


UPDATE 1

what i couldnt understand is how can i access the variable months without calling it like this in the above class

static void Main()
{

MonthCollection collection = new MonthCollection();
// Consumes values generated from collection's GetEnumerator method
foreach (string n in collection.months)
Console.Write(n + " ");
Console.WriteLine("\n");
}


instead of like this

MonthCollection collection = new MonthCollection();
method
foreach (string n in collection)
Console.Write(n + " ");
Console.WriteLine("\n");

Answer

foreach loop does not require a collection, all it needs is IEnumerable implementation with GetEnumerator() method returning IEnumerator.

C# compiler produces the remaining "magic" - a hidden temporary variable to reference the enumerator object, the code to start enumeration, the code to get each item, and the code to see when enumeration is over.

In your case C# relies on your implementation to get to the content of the months array using another piece of "magic" - the yield return construct. This construct lets you implement GetEnumerator() without writing a separate class for it. The construct is converted to a class with a state machine, letting you "return" objects from the middle of a loop, and then restart back where it was when the next item is requested from the iterator.

The objects returned from yield return do not have to come from a collection. You can even mix objects from a collection with other objects, for example

public IEnumerator GetEnumerator() {
    // Generates values from the collection
    foreach (string element in months) {
        yield return element + " will start soon!";
        yield return element;
        yield return element + " has ended.";
    }
}

foreach loop access variable months indirectly, through your code that implements GetEnumerator().

Comments