newprint
newprint

Reputation: 7136

Why return IEnumerable for named iterators?

New to C#: could someone explain, why named iterators (term I found in MS C# Programming Guide) should to return IEnumerable?

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

It makes sense when class implements GetEnumerator() of the IEnumerable or IEnumerable<T> interfaces, but I am not sure why and how it works for methods and properties.

Edit: it was pointed out that my question is not well posed.(Maybe, I am still thinking in terms of C++)

Clarification: in order to use foreach loop on object of some class, class needs to implement IEnumerable and IEnumerator interfaces or yield return(i.e. compiler takes care of everything). Example:

class ListClass : System.Collections.IEnumerable
{
    public System.Collections.IEnumerator GetEnumerator() {...//uses yield return }  
    ....
}
    ....
    ListClass listClass1 = new ListClass(); // class implements IEnumerable interface method 

    foreach (int i in listClass1) {...}

In the example above, GetEnumerator() was implemented, which as far as I understood, must be implemented, if you want your object to run in foreach loop.

However, when named iterator is implemented (code above), GetEnumerator() is not required and only IEnumerable<T>(or non generic) must be returned by method/property.

Hopefully, this clarifies my question.

Upvotes: 2

Views: 315

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1063338

Actually, I'd say that it should return IEnumerable<T> - or IEnumerable<int> in this case. But the reason is because that works, and because iterator blocks (methods with the yield syntax) can implement that pattern for you. If you wanted to do something else that worked with foreach, you would have to write your own CustomEnumerable / CustomIterator, and manually implement the state-machine in the iterator, using the MoveNext() / Current pattern, remembering to put any finally etc in the Dispose(). Lots of work; not much gain.


Edit: here's a poorly written, probably buggy implementation of doing it the hard way, i.e. returning a custom iterator by hand. Note that returning an int[] or List<int> would be really simple, but significantly changes the semantic: it is no longer a spooling iterator, which is what yield creates:

static CustomEnumerable SampleIteratorHard(int start, int end)
{
    return new CustomEnumerable(start, end);
}
class CustomEnumerable
{
    private readonly int start, end;
    public CustomEnumerable(int start, int end)
    {
        this.start = start;
        this.end = end;
    }
    public CustomIterator GetEnumerator()
    {
        return new CustomIterator(start, end);
    }
}
class CustomIterator
{
    private readonly int start, end;
    int i, state, value;
    public CustomIterator(int start, int end)
    {
        this.start = start;
        this.end = end;
        start = 0;
    }
    public bool MoveNext()
    {
        if (state == 2) return false;
        if (state == 0)
        {
            i = start - 1; // to leave space for the ++
            state = 1;
        }
        if (state == 1)
        {
            i++;
            if (i <= end)
            {
                value = i;
                return true;
            }
        }
        state = 2;
        return false;
    }
    public int Current {  get {  return value; } }
}

Upvotes: 4

Related Questions