Question

I'm trying to implement the Iterator pattern. Basically, from what I understand, it makes a class "foreachble" and makes the code more secure by not revealing the exact collection type to the user.

I have been experimenting a bit and I found out that if I implement IEnumerator GetEnumerator() in my class, I get the desired result ... seemingly sparing the headache of messing around with realizing interfaces.

Here is a glimpse to what I mean:

public class ListUserLoggedIn
{
    /*
        stuff
    */
    
    public List<UserLoggedIn> UserList { get; set; }

    public IEnumerator<UserLoggedIn> GetEnumerator()
    {
        foreach (UserLoggedIn user in this.UserList)
        {
            yield return user;
        }
    }

    public void traverse()
    {
        foreach (var item in ListUserLoggedIn.Instance)
        {
            Console.Write(item.Id);
        }
    }
}

I guess my question is, is this a valid example of Iterator? If yes, why is this working, and what can I do to make the iterator return only a part or an anonymous object via "var". If not, what is the correct way ...

Was it helpful?

Solution

First a smaller and simplified self-contained version:

class Program
{
    public IEnumerator<int> GetEnumerator()  // IEnumerable<int> works too.
    {
        for (int i = 0; i < 5; i++)     
            yield return i;     
    }

    static void Main(string[] args)
    {
        var p = new Program();

        foreach (int x in p)
        {
            Console.WriteLine(x);
        }
    }
}

And the 'strange' thing here is that class Program does not implement IEnumerable.

The specs from Ecma-334 say:

§ 8.18 Iterators
The foreach statement is used to iterate over the elements of an enumerable collection. In order to be enumerable, a collection shall have a parameterless GetEnumerator method that returns an enumerator.

So that's why foreach() works on your class. No mention of IEnumerable. But how does the GetEnumerator() produce something that implements Current and MoveNext ? From the same section:

An iterator is a statement block that yields an ordered sequence of values. An iterator is distinguished from a normal statement block by the presence of one or more yield statements

It is important to understand that an iterator is not a kind of member, but is a means of implementing a function member

So the body of your method is an iterator-block, the compiler checks a number of constraints (the method must return an IEnumerable or IEnumerator) and then implements the IEnumerator members for you.

And to the deeper "why", I just learned something too. Based on an annotation by Eric Lippert in "The C# Programming Language 3rd", page 369:

This is called the "pattern-based approach" and it dates from before generics. An interface based approach in C#1 would have been totally based on passing object around and value types would always have had to be boxed. The pattern approach allows

foreach (int x in myIntCollection)

without generics and without boxing. Neat.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top