2

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 ...

R.J. Dunnill
  • 2,049
  • 3
  • 10
  • 21
  • your code looks OK, doesn't it work? – King King Aug 28 '13 at 21:19
  • it works... but why? i thought a design pattern should have a bit more meat to it.. – Idan Boadana Aug 28 '13 at 21:21
  • Design patterns are just approaches to a certain design issue. Sometimes the issue is resolved with minor changes like this, or a singleton. – Jeroen Vannevel Aug 28 '13 at 21:24
  • 1
    The "why" is built-in in the framework. A design decision. And while it's not strictly necessary to implement `IEnumerable<>` it certainly is a good idea (be it for Linq alone). – H H Aug 28 '13 at 21:25
  • OK, but im having trouble grasping whats happening behind the scenes.. what exactly am i returning as an enumerator?, when i go to IEnumerator<> definition, i see "T Current { get; }" but what about next and reset? – Idan Boadana Aug 28 '13 at 21:30
  • 1
    @IdanBoadana: Maybe I'm misinterpreting what you're asking, but the `yield return` syntax was added as a convenience/sanity feature. Behind the scenes, the C# compiler is creating a basic iterator implementation _for you_ and compiling it in on the fly. For example, if you executed: `myListUserLoggedIn.GetEnumerator().GetType()` you'd find it produced some anonymously named type (maybe something like `d__0`, but with additional flavour that I can't recall off the top of my head right now) – Chris Sinclair Aug 28 '13 at 21:54

1 Answers1

5

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.

Community
  • 1
  • 1
H H
  • 263,252
  • 30
  • 330
  • 514
  • Wow, something new everyday. What's the a reason why `foreach` works on classes that simply _define_ a method of the correct signature and not implement `IEnumerable`? EDIT: I mean, the reasoning beyond "the specification says so". I understand that this isn't always answerable. :) – Chris Sinclair Aug 28 '13 at 22:14
  • Thanks allot henk! so if im getting the point, the yield statement creates a new Enumerable object from the type of the left side of the equation, which implements all the necessary logic? – Idan Boadana Aug 28 '13 at 22:19
  • Found an 'annotation, I'll add to my answer. – H H Aug 28 '13 at 22:20
  • @IdanBoadana - Yes, kind-of. The `yield` statements invokes a lot of compiler magic for the surrounding block. – H H Aug 28 '13 at 22:21
  • Regarding the annotation: hey, that's not a bad idea. I wonder if generics had existed as of C#1.0 whether or not it'd be implemented differently. – Chris Sinclair Aug 28 '13 at 22:43
  • 1
    See http://blogs.msdn.com/b/ericlippert/archive/2011/06/30/following-the-pattern.aspx for additional thoughts on this. – Eric Lippert Aug 29 '13 at 15:18