问题
Application accidentally crashed with stack overflow error. After research I found that the reason of crash is following code:
foreach (var item in items)
{
result = result.Concat(item.Data);
}
This is concatenation of multiple IEnumerable
s. Application crashed when items
contained 10,000 elements.
SelectMany
fixed this issue. But still...
Why Concat
extension causes stack overflow here?
回答1:
Remember that the result of Concat
is not a collection - it's a query.
So your "result" is effectively
Enumerable.Concat(item10000.Data,
Enumerable.Concat(item9999.Data,
....
Enumerable.Concat(item2.Data,
item1.Data))));
When the nested queries are generated it causes a stack overflow.
Another option would be to create a list and add to it each time:
var list = new List<something>();
foreach (var item in items)
{
list.AddRange(item.Data);
}
Which is essentially what SelectMany
does (but with a deferred enumerator, not a List
):
result = items.SelectMany(item => item.Data);
回答2:
When you ask the 10,000th concat to get its result it'll ask the 9,999th concat operation for its result, with the intention of yielding a few more values after it's done, and then then 9,999th concat operation will ask the 9,998th concat for its result, which will ask the 9,997th concat for its result, and eventually you either get down to the 1st concat, or you run out of stack space.
As you mentioned right in the question, SelectMany
is the correct operation to flatten a sequence of sequences.
来源:https://stackoverflow.com/questions/26065930/concat-causes-stack-overflow-if-called-multiple-times