Currently I’m reading a collection of items from a stream. I do this as following:
public class Parser{
private TextReader _reader; //Get set in Constructor
private IEnumerable<Item> _items;
public IEnumerable<Item> Items{
get{
//I >>thought<< this would prevent LoadItems() from being called twice.
return _items ?? (_items = LoadItems());
}
}
public IEnumerable<Item> LoadItems(){
while(_reader.Peek() >= 0){
yield return new Item(_reader.ReadLine()); //Actually it's a little different
}
}
}
Let say I have a stream which contains two items, and I do the following:
var textReader = //Load textreader here
var parser = new Parser(textReader);
var result1 = parser.Items.Count();
var result2 = parser.Items.Count();
Now result1 is 2, while result2 is one.
Now I noticed, that my null check is useless? It seems that everytime I call that function it gets yielded anyway.
Can someone explain to me why this is? And what would be the best solution for this situation (please tell me if what I’m doing is complete crap :P).
Because
LoadItemsis a lazy enumerable (usesyield) and you are assigning it to a field, it means that every time you enumerate_itemsyou are actually causing the loop withinLoadItems()to be run again, i.e. (Enumerable.Countcreates a newEnumeratoreach time which cause theLoadItemsbody to be run again). As you are not creating the reader afresh each time withinLoadItemsits cursor will be positioned at the end of the stream so will likely not be able to read any more lines — I suspect that it is returningnulland your your singleItemobject returned on the second call contains anullstring.Solutions to this would be to ‘realise’ the result of
LoadItemsby callingEnumerable.ToListwhich will give you a concrete list:Or seeking the reader back to the beginning of the stream (if possible) such that
LoadItemscan run again each time identically.But I would recommend you simply get rid of the
yielding in this case and return a concrete list as there is little benefit so you are paying the complexity price for no gain.