I come from the world of Python and am trying to create a "generator" method in C#. I'm parsing a file in chunks of a specific buffer size, and only want to read and store the next chunk one at a time and yield it in a foreach
loop. Here's what I have so far (simplified proof of concept):
class Page
{
public uint StartOffset { get; set; }
private uint currentOffset = 0;
public Page(MyClass c, uint pageNumber)
{
uint StartOffset = pageNumber * c.myPageSize;
if (StartOffset < c.myLength)
currentOffset = StartOffset;
else
throw new ArgumentOutOfRangeException("Page offset exceeds end of file");
while (currentOffset < c.myLength && currentOffset < (StartOffset + c.myPageSize))
// read data from page and populate members (not shown for MWE purposes)
. . .
}
}
class MyClass
{
public uint myLength { get; set; }
public uint myPageSize { get; set; }
public IEnumerator<Page> GetEnumerator()
{
for (uint i = 1; i < this.myLength; i++)
{
// start count at 1 to skip first page
Page p = new Page(this, i);
try
{
yield return p;
}
catch (ArgumentOutOfRangeException)
{
// end of available pages, how to signal calling foreach loop?
}
}
}
}
I know this is not perfect since it is a minimum working example (I don't allow many of these properties to be set publicly, but for keeping this simple I don't want to type private members and properties).
However, my main question is how do I let the caller looping over MyClass with a foreach statement know that there are no more items left to loop through? Is there an exception I throw to indicate there are no elements left?
IEnumerable<BTreePage>
; enumerables are easier to consume. – ForepeakIEnumerator<T>.MoveNext
is what tells the caller to stop iterating. This is implemented for you when you useyield return
. If you wish to explicitly stop you can useyield break
. – DurbarIEnumerable<T>
vs.IEnumerator<T>
. – ForepeakIEnumerable
is the generator, or the list, andIEnumerator
would be the thing you get when you calliter()
on it (the technical thing). Most of the time, you would want theIEnumerable
since that’s easier to consume (e.g. using aforeach
loop). – Forepeakforeach (Page p in myClass)
? – RhythmicsIEnumerator<Page> GetEnumerator()
, change the signature toIEnumerable<Page> GetPages()
(use a proper name too). The implementation is the same. – Forepeakforeach
to work, you need to returnIEnumerable
instead ofIEnumerator
. – Forepeak