Pembaruan 7/17/2012: Rupanya pada C # 5.0, perilaku yang foreach
dijelaskan di bawah ini telah diubah dan " penggunaan foreach
variabel iterasi dalam ekspresi lambda bersarang tidak lagi menghasilkan hasil yang tidak terduga. " Jawaban ini tidak berlaku untuk C # ≥ 5.0 .
@John Skeet dan semua orang yang lebih suka kata kunci foreach.
Masalah dengan "foreach" dalam C # sebelum 5.0 , adalah bahwa itu tidak konsisten dengan cara yang setara "untuk pemahaman" bekerja dalam bahasa lain, dan dengan bagaimana saya mengharapkannya bekerja (pendapat pribadi yang dinyatakan di sini hanya karena orang lain telah menyebutkan mereka pendapat tentang keterbacaan). Lihat semua pertanyaan tentang " Akses ke penutupan yang dimodifikasi " serta " Menutup variabel loop yang dianggap berbahaya ". Ini hanya "berbahaya" karena cara "foreach" diimplementasikan dalam C #.
Ambil contoh berikut menggunakan metode ekstensi yang setara secara fungsional dengan yang ada di jawaban @Fredrik Kalseth.
public static class Enumerables
{
public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action)
{
foreach (T item in @this)
{
action(item);
}
}
}
Permintaan maaf untuk contoh yang terlalu dibuat-buat. Saya hanya menggunakan Observable karena tidak sepenuhnya diambil untuk melakukan sesuatu seperti ini. Jelas ada cara yang lebih baik untuk membuat ini dapat diamati, saya hanya berusaha menunjukkan suatu hal. Biasanya kode yang berlangganan observable dijalankan secara tidak serempak dan berpotensi di utas lainnya. Jika menggunakan "foreach", ini bisa menghasilkan hasil yang sangat aneh dan berpotensi non-deterministik.
Tes berikut menggunakan metode ekstensi "ForEach" lolos:
[Test]
public void ForEachExtensionWin()
{
//Yes, I know there is an Observable.Range.
var values = Enumerable.Range(0, 10);
var observable = Observable.Create<Func<int>>(source =>
{
values.ForEach(value =>
source.OnNext(() => value));
source.OnCompleted();
return () => { };
});
//Simulate subscribing and evaluating Funcs
var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();
//Win
Assert.That(evaluatedObservable,
Is.EquivalentTo(values.ToList()));
}
Berikut ini gagal dengan kesalahan:
Diharapkan: setara dengan <0, 1, 2, 3, 4, 5, 6, 7, 8, 9> Tetapi adalah: <9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9>
[Test]
public void ForEachKeywordFail()
{
//Yes, I know there is an Observable.Range.
var values = Enumerable.Range(0, 10);
var observable = Observable.Create<Func<int>>(source =>
{
foreach (var value in values)
{
//If you have resharper, notice the warning
source.OnNext(() => value);
}
source.OnCompleted();
return () => { };
});
//Simulate subscribing and evaluating Funcs
var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();
//Fail
Assert.That(evaluatedObservable,
Is.EquivalentTo(values.ToList()));
}
ForEach()
.