Bu uyarının iki bölümü vardır. İlk olarak...
Kapanışta foreach değişkenine erişim
... bu kendiliğinden geçersiz değildir, ancak ilk bakışta mantıksızdır. Doğru yapmak da çok zor. (Öyle ki, aşağıda bağlantı verdiğim makale bunu "zararlı" olarak tanımlıyor.)
Sorgunuzu alın, aldığınız kodun temelde C # derleyicisinin (C # 5'ten önce) foreach1 için ürettiği şeyin genişletilmiş bir biçimi olduğuna dikkat edin :
Neden [aşağıdakilerin] geçerli olmadığını [anlamıyorum]:
string s; while (enumerator.MoveNext()) { s = enumerator.Current; ...
Sözdizimsel olarak geçerlidir. Ve sen döngü içinde yapıyoruz bütün kullanıyorsa değerini ait so zaman her şey iyidir. Ancak kapamak ssezgisel olmayan davranışlara yol açacaktır. Aşağıdaki koda bir göz atın:
var countingActions = new List<Action>();
var numbers = from n in Enumerable.Range(1, 5)
select n.ToString(CultureInfo.InvariantCulture);
using (var enumerator = numbers.GetEnumerator())
{
string s;
while (enumerator.MoveNext())
{
s = enumerator.Current;
Console.WriteLine("Creating an action where s == {0}", s);
Action action = () => Console.WriteLine("s == {0}", s);
countingActions.Add(action);
}
}
Bu kodu çalıştırırsanız, aşağıdaki konsol çıktısını alırsınız:
Creating an action where s == 1
Creating an action where s == 2
Creating an action where s == 3
Creating an action where s == 4
Creating an action where s == 5
Beklediğiniz bu.
Muhtemelen beklemediğiniz bir şey görmek için yukarıdaki koddan hemen sonra aşağıdaki kodu çalıştırın :
foreach (var action in countingActions)
action();
Aşağıdaki konsol çıktısını alacaksınız:
s == 5
s == 5
s == 5
s == 5
s == 5
Neden? Çünkü hepsi aynı şeyi yapan beş işlev yarattık: değerini yazdırın s(kapattığımız). Gerçekte, bunlar aynı işlevdir ("Yazdır s", "Yazdır s", "Yazdır"s " ...).
Onları kullanmaya gittiğimiz noktada, tam olarak istediğimiz şeyi yapıyorlar: değerini yazdırın s. Son bilinen değerine bakarsanız, sbunun olduğunu görürsünüz 5. Böylece s == 5konsola beş kez baskı alıyoruz .
Bu tam olarak istediğimiz şeydi, ama muhtemelen istediğimiz şey değil.
Uyarının ikinci kısmı ...
Derleyicinin farklı sürümleriyle derlendiğinde farklı davranışlara sahip olabilir.
... ne olduğu. C # 5 ile başlayarak, derleyici bunun şu yolla olmasını "engelleyen" farklı kod üretir:foreach .
Bu nedenle, aşağıdaki kod, derleyicinin farklı sürümleri altında farklı sonuçlar üretecektir:
foreach (var n in numbers)
{
Action action = () => Console.WriteLine("n == {0}", n);
countingActions.Add(action);
}
Sonuç olarak, R # uyarısını da üretecektir :)
Yukarıdaki ilk kod parçacığım, kullanmadığım için derleyicinin tüm sürümlerinde aynı davranışı sergileyecektir foreach(bunun yerine, onu C # 5 öncesi derleyicilerin yaptığı şekilde genişlettim).
Bu CLR versiyonu için mi?
Burada ne istediğinden pek emin değilim.
Eric Lippert'in gönderisi, değişikliğin "C # 5'te" gerçekleştiğini söylüyor. Yanimuhtemelen .NET 4.5 veya sonrasını hedeflemeniz gerekir Yeni davranışı elde etmek için C # 5 veya sonraki bir derleyici ile ve bundan önceki her şey eski davranışı alır.
Ancak açık olmak gerekirse, bu .NET Framework sürümünün değil derleyicinin bir işlevidir.
IL ile bir alakası var mı?
Farklı kod farklı IL üretir, dolayısıyla bu anlamda üretilen IL için sonuçlar vardır.
1 foreach , yorumunuzda gönderdiğiniz koddan çok daha yaygın bir yapıdır. Sorun tipik olarak foreachmanuel numaralandırma yoluyla değil, kullanımından kaynaklanmaktadır . Bu nedenle foreachC # 5'te yapılan değişiklikler bu sorunu önlemeye yardımcı olur, ancak tamamen değil.