2014-09-26 21 views
1

應用程序意外地因堆棧溢出錯誤而崩潰。經研究發現,墜機原因如下:如果被多次調用,Concat會導致堆棧溢出

foreach (var item in items) 
{ 
    result = result.Concat(item.Data); 
} 

這是多個IEnumerable s的串接。當items包含10,000個元素時,應用程序崩潰。

SelectMany解決了這個問題。但仍...

爲什麼Concat擴展會導致堆棧溢出?

+4

再一次說明:如果我能告訴大家關於LINQ的一件事,那就是*查詢表達式的結果是可以執行查詢的對象*,而不是執行查詢*的結果。 – 2014-09-26 18:29:01

+1

另請參閱http://en.wikipedia.org/wiki/Joel_Spolsky的Schlemiel-the-painter部分。您已經實現了此算法的延期執行版本。 – 2014-09-26 18:31:35

回答

8

請記住,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); 
1

當你要求第10000個連續獲得結果時,它會詢問第9,999個連續操作的結果爲其結果爲,意圖在完成後產生更多的值,然後第9,999個連續操作將詢問第9,998個連續的結果,它將要求第9,997個連續的結果,並且最終要麼下降到第1個連續,要麼你的堆棧空間用完。

正如您在問題中提到的那樣,SelectMany是將序列平坦化的正確操作。