應用程序意外地因堆棧溢出錯誤而崩潰。經研究發現,墜機原因如下:如果被多次調用,Concat會導致堆棧溢出
foreach (var item in items)
{
result = result.Concat(item.Data);
}
這是多個IEnumerable
s的串接。當items
包含10,000個元素時,應用程序崩潰。
SelectMany
解決了這個問題。但仍...
爲什麼Concat
擴展會導致堆棧溢出?
應用程序意外地因堆棧溢出錯誤而崩潰。經研究發現,墜機原因如下:如果被多次調用,Concat會導致堆棧溢出
foreach (var item in items)
{
result = result.Concat(item.Data);
}
這是多個IEnumerable
s的串接。當items
包含10,000個元素時,應用程序崩潰。
SelectMany
解決了這個問題。但仍...
爲什麼Concat
擴展會導致堆棧溢出?
請記住,Concat
的結果不是一個集合 - 這是一個查詢。
所以你的「結果」實際上是
Enumerable.Concat(item10000.Data,
Enumerable.Concat(item9999.Data,
....
Enumerable.Concat(item2.Data,
item1.Data))));
當嵌套查詢生成它導致堆棧溢出。
另一種選擇是創建一個列表,每次添加到它:
var list = new List<something>();
foreach (var item in items)
{
list.AddRange(item.Data);
}
這基本上是什麼SelectMany
做(但延期枚舉,而不是一個List
):
result = items.SelectMany(item => item.Data);
當你要求第10000個連續獲得結果時,它會詢問第9,999個連續操作的結果爲其結果爲,意圖在完成後產生更多的值,然後第9,999個連續操作將詢問第9,998個連續的結果,它將要求第9,997個連續的結果,並且最終要麼下降到第1個連續,要麼你的堆棧空間用完。
正如您在問題中提到的那樣,SelectMany
是將序列平坦化的正確操作。
再一次說明:如果我能告訴大家關於LINQ的一件事,那就是*查詢表達式的結果是可以執行查詢的對象*,而不是執行查詢*的結果。 – 2014-09-26 18:29:01
另請參閱http://en.wikipedia.org/wiki/Joel_Spolsky的Schlemiel-the-painter部分。您已經實現了此算法的延期執行版本。 – 2014-09-26 18:31:35