3 回答

TA貢獻1776條經(jīng)驗 獲得超12個贊
除非您只需要一個數(shù)組來滿足其他約束,否則您應該使用它ToList
。在大多數(shù)情況下ToArray
會分配更多的內(nèi)存ToList
。
兩者都使用數(shù)組進行存儲,但ToList
具有更靈活的約束。它需要數(shù)組至少與集合中元素的數(shù)量一樣大。如果陣列較大,那不是問題。但是,ToArray
需要將數(shù)組的大小精確地調(diào)整為元素的數(shù)量。
為了滿足這種約束,ToArray
通常會進行一次分配ToList
。一旦它有一個足夠大的數(shù)組,它就會分配一個完全正確大小的數(shù)組,并將元素復制回該數(shù)組。唯一能避免這種情況的是當數(shù)組的增長算法恰好與需要存儲的元素數(shù)量一致時(絕對是少數(shù))。
編輯
有幾個人問我在List<T>
數(shù)值中有多余的未使用內(nèi)存的后果。
這是一個有效的問題。如果創(chuàng)建的集合是長期存在的,在創(chuàng)建后永遠不會被修改并且很有可能在Gen2堆中著陸,那么您可能最好先采取額外的分配ToArray
。
總的來說,雖然我發(fā)現(xiàn)這是罕見的情況。更常見的是看到很多ToArray
調(diào)用立即傳遞給其他短暫的內(nèi)存使用,在這種情況下ToList
顯然更好。
這里的關(guān)鍵是剖析,剖析,然后再詳細介紹一些。

TA貢獻1993條經(jīng)驗 獲得超6個贊
性能差異將是微不足道的,因為它List<T>
是作為動態(tài)大小的陣列實現(xiàn)的。調(diào)用ToArray()
(使用內(nèi)部Buffer<T>
類來增長數(shù)組)或ToList()
(調(diào)用List<T>(IEnumerable<T>)
構(gòu)造函數(shù))將最終成為將它們放入數(shù)組并增長數(shù)組直到它適合所有數(shù)組的問題。
如果您希望具體確認這一事實,請查看Reflector中相關(guān)方法的實現(xiàn) - 您將看到它們歸結(jié)為幾乎完全相同的代碼。

TA貢獻1871條經(jīng)驗 獲得超13個贊
其他(好)答案集中在將發(fā)生的微觀性能差異上。
此信息僅僅是一個補充提語義差別之間存在IEnumerator<T>(產(chǎn)生由陣列T[])相比于由一個返回List<T>。
通過示例最佳說明:
IList<int> source = Enumerable.Range(1, 10).ToArray(); // try changing to .ToList()
foreach (var x in source)
{
if (x == 5)
source[8] *= 100;
Console.WriteLine(x);
}
上面的代碼將運行,沒有異常并產(chǎn)生輸出:
1
2
3
4
五
6
7
8
900
10
這表明IEnumarator<int>由an返回的數(shù)據(jù)int[]不會跟蹤自創(chuàng)建枚舉器以來數(shù)組是否已被修改。
請注意,我將局部變量聲明source為IList<int>。通過這種方式,我確保C#編譯器不會將foreach語句優(yōu)化為等同于for (var idx = 0; idx < source.Length; idx++) { /* ... */ }循環(huán)的東西。如果我使用C#編譯器可能會這樣做var source = ...;。在我當前版本的.NET框架中,這里使用的實際枚舉器是非公共引用類型,System.SZArrayHelper+SZGenericArrayEnumerator`1[System.Int32]但當然這是一個實現(xiàn)細節(jié)。
現(xiàn)在,如果我改變.ToArray()成.ToList(),我只得到:
1
2
3
4
五
隨后是一個System.InvalidOperationException爆炸性的說法:
收集被修改; 枚舉操作可能無法執(zhí)行。
在這種情況下,底層枚舉器是public mutable value-type System.Collections.Generic.List`1+Enumerator[System.Int32](IEnumerator<int>在這種情況下,盒子裝在盒子里,因為我使用IList<int>)。
總之,由枚舉器生成的枚舉器List<T>會跟蹤列表在枚舉期間是否發(fā)生更改,而生成的枚舉T[]器則不會。因此,在.ToList()和之間選擇時要考慮這種差異.ToArray()。
人們經(jīng)常添加一個額外的 .ToArray()或.ToList()繞過一個集合來跟蹤它是否在枚舉器的生命周期中被修改。
(如果有人想知道如何在List<>跟蹤上收集是否被修改,有一個私人領(lǐng)域_version在這個類,這是每次改變的List<>更新。)
- 3 回答
- 0 關(guān)注
- 1287 瀏覽
添加回答
舉報