After studying the two answers that were given and a little bored, I came up with a small program that better illustrates the problem.
private int GetFirst(IEnumerable<int> items, int foo) { Console.WriteLine("GetFirst {0}", foo); var rslt = items.First(i => i == foo); Console.WriteLine("GetFirst returns {0}", rslt); return rslt; } private IEnumerable<int> GoNuts(IEnumerable<int> items) { items = items.Select(item => { Console.WriteLine("Select item = {0}", item); return GetFirst(items, item); }); return items; }
If you call this with:
var newList = GoNuts(new[]{1, 2, 3, 4, 5, 6});
You will receive this output several times until you get a StackOverflowException .
Select item = 1 GetFirst 1 Select item = 1 GetFirst 1 Select item = 1 GetFirst 1 ...
What this shows is exactly what dasblinkenlight made clear in its updated answer: the request goes into an infinite loop, trying to get the first element.
Let's write GoNuts little differently:
private IEnumerable<int> GoNuts(IEnumerable<int> items) { var originalItems = items; items = items.Select(item => { Console.WriteLine("Select item = {0}", item); return GetFirst(originalItems, item); }); return items; }
If you run this, it will be successful. What for? Since in this case it is clear that the GetFirst call passes a reference to the source elements that were passed to the method. In the first case, GetFirst passes a link to a new collection of items that is not yet implemented. In turn, GetFirst says: "Hey, I need to list this collection." And so the first recursive call begins, which ultimately leads to a StackOverflowException .
Interestingly, I was right and wrong when I said that he was consuming his own result. Select consumes the original input, as you would expect. First tries to use the output.
There are many lessons here. For me, the most important thing is "do not change the value of the input parameters."
Thanks to dasblinkenlight, D Stanley and Lucas Trzesniewski for their help.