Reputation: 7136
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
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