I was making a simple test for running a validation method and came across this strange situation.
public IEnumerable<int> ints (List<int> l)
{
if(false)yield return 6;
l.Add(4);
}
void Main()
{
var a = new List<int>();
var b = new List<int>();
for( int i = 0; i < 4; i++ ){
a.Add(i);
b.Add(i);
}
a.AddRange(ints(a));
ints(b);
Console.WriteLine(a);
Console.WriteLine(b);
}
Once this code runs, a
will contain [0,1,2,3,4]
. However, b
will contain [0,1,2,3]
. Why did calling the method as an argument in AddRange
allow the list to be passed by reference? Or if that didn't happen, what did?
ints(b)
call does not enumerate IEnumerable
, so code never reaches l.Add(4)
line, unlike in AddRange
case which enumerates all items to add them to the list.
To see it called for b
case enumerate result manually:
ints(b).ToList();
IEnumerable<T>
implemented via functions are not executing body of the function before enumeration starts - the code is actually transformed by compiler into class with states to support true lazy evaluation of enumerable (details can be found in multiple articles, i.e. Iterator Pattern demystified - link provided by Tim Schmelter).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With