Reputation: 2124
Given a class that holds (and initializes once) an IEnumerable<T>
:
public class MyClass
{
public MyClass()
{
Values = Sequence(0, 10).Select(i => Convert(i));
}
public IEnumerable<string> Values { get; private set; }
private static string Convert(int i)
{
string iStr = i.ToString();
Console.WriteLine("Convert: " + iStr);
return iStr;
}
private static IEnumerable<int> Sequence(int start, int end)
{
return Enumerable.Range(start, end - start + 1);
}
}
Why does every usage that requires the evaluated values of myClass.Values
re-evaluates them?
e.g.:
static void Main(string[] args)
{
MyClass myClass = new MyClass();
foreach (var v in myClass.Values)
{
Console.WriteLine(v);
}
foreach (var v in myClass.Values)
{
Console.WriteLine("Value: " + v);
}
}
This will print 20 times "Convert: " and "Value: ".
I know that saving the Values as a list (with ToList()
for example) will solve this, but it was my understanding that once Values are evaluated the values will be used from memory (as if I used Lazy<T>
and then Values.value
), why is this not the same case?
Upvotes: 1
Views: 426
Reputation: 62488
This is the normal behavior of IEnumerable<T>
. Your projection call Select
is actually invoked for every item in the collection (but apparently you only called it once)when each item is called in the foreach
loop.
So when you are looping with foreach
loop the IEnumerable
object is calling the enumerator and getting the next item if any available in the collection something similar to state machine.
It keeps track of the current item being enumerated when next item is request in the foreach loop it is called again so resulting the Convert
method to be called again.
Upvotes: 1