Jika saya menggunakan:
var strings = new List<string> { "sample" };
foreach (string s in strings)
{
Console.WriteLine(s);
strings.Add(s + "!");
}
bagian Add
dalam foreach
melempar sebuah InvalidOperationException (Koleksi telah dimodifikasi; operasi pencacahan tidak dapat dijalankan), yang saya anggap logis, karena kita menarik permadani dari bawah kaki kita.
Namun, jika saya menggunakan:
var strings = new List<string> { "sample" };
strings.ForEach(s =>
{
Console.WriteLine(s);
strings.Add(s + "!");
});
itu segera menembak dirinya sendiri di kaki dengan perulangan sampai melempar OutOfMemoryException.
Ini adalah kejutan bagi saya, karena saya selalu berpikir bahwa List.ForEach hanyalah pembungkus untuk foreach
atau untuk for
.
Adakah yang punya penjelasan tentang bagaimana dan mengapa perilaku ini?
(Terinspirasi oleh perulangan ForEach untuk Daftar Generik yang berulang tanpa henti )
foreach
atau untukfor
." Itu masih bisa digunakanfor
. Anda dapat melakukan tindakan yang sama dalam satufor
putaran dan menghasilkan OutOfMemoryException yang sama sebagai hasilnya.Jawaban:
Itu karena
ForEach
metode ini tidak menggunakan pencacah, itufor
mengulang melalui item dengan perulangan:public void ForEach(Action<T> action) { if (action == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); } for (int i = 0; i < this._size; i++) { action(this._items[i]); } }
(kode diperoleh dengan JustDecompile)
Karena enumerator tidak digunakan, enumerator tidak pernah memeriksa apakah daftar telah berubah, dan kondisi akhir dari
for
perulangan tidak pernah tercapai karena_size
bertambah pada setiap iterasi.sumber
_size
menghitungnya? Jika itu hanya dihitung sebelumnya maka jika harus dijalankan sekali untuk contoh saya. Ini jelas disegarkan entah bagaimana._version
variabel privatList<T>
yang dapat mendeteksi jenis skenario ini, karena diperbarui pada operasi yang mengubah daftar itu sendiri.List<T>.ForEach
diimplementasikan melaluifor
dalam, sehingga tidak menggunakan pencacah dan memungkinkan untuk memodifikasi koleksi.sumber
Karena ForEach yang dilampirkan ke kelas Daftar secara internal menggunakan perulangan for yang langsung dilampirkan ke anggota internalnya - yang dapat Anda lihat dengan mengunduh kode sumber untuk kerangka .NET.
http://referencesource.microsoft.com/netframework.aspx
Dimana foreach loop adalah yang pertama dan terutama pengoptimalan compiler tetapi juga harus beroperasi terhadap koleksi sebagai pengamat - jadi jika koleksi diubah, pengecualian akan muncul.
sumber
Add
barisstrings.Insert(0, s + "!")
hanya dengan mencetak 'sampel'. Aneh bahwa ini tidak disebutkan sama sekali dalam dokumentasi.Kami tahu tentang masalah ini, itu adalah kekeliruan ketika aslinya ditulis. Sayangnya, kami tidak dapat mengubahnya karena sekarang akan mencegah kode yang sebelumnya berfungsi ini untuk berjalan:
var list = new List<string>(); list.Add("Foo"); list.Add("Bar"); list.ForEach((item) => { if(item=="Foo") list.Remove(item); });
Kegunaan metode ini sendiri dipertanyakan seperti yang ditunjukkan oleh Eric Lippert , jadi kami tidak menyertakannya untuk .NET untuk aplikasi gaya Metro (yaitu aplikasi Windows 8).
David Kean (Tim BCL)
sumber