2013-04-28 9 views
39

Bu iyi çalışır içinde (beklendiği gibi anlamına gelir) C# 5.0:Yakalanan Kapatma (Döngü Değişken) C# 5.0

var actions = new List<Action>(); 
foreach (var i in Enumerable.Range(0, 10)) 
{ 
    actions.Add(() => Console.WriteLine(i)); 
} 
foreach (var act in actions) act(); 

Baskılar 9'a kadar 0 Ama bu bir 10. yıl için 10 kez gösterilmiştir:

var actions = new List<Action>(); 
for (var i = 0; i < 10; i++) 
{ 
    actions.Add(() => Console.WriteLine(i)); 
} 
foreach (var act in actions) act(); 

Soru: Bu, C# sürümlerinde 5.0'dan önceki bir sorun oldu; bu nedenle, kapanış için bir döngü-yerel yer tutucusu kullanmak zorundaydık ve şimdi "foreach" döngülerinde C# 5.0'de düzeltildi. Ama "için" döngülerinde değil!

Bunun arkasındaki mantık nedir (sorunu for döngülerinde de sabitlemiyor)?

+2

"Bunun anlamı, '' döngüleri için de sabitlenmemesinin nedenleri '? –

+0

İlk durumda bile çalışmasına şaşırdım ... foreach/for. Artık var olmamalıyım. Bana göre bu çok "tehlikeli" bir tasarım. – LightStriker

+0

@LightStriker: No; bu bir özellik. Buna kapanma deniyor. – SLaks

cevap

37

Bunun arkasındaki mantık nedir?

"Neden for döngülerinde neden değişmedi?"

Yanıt, for döngüleri için mevcut davranışların mükemmel bir anlam ifade etmesidir. Eğer içine for döngü uymazsanız:

  • başlatıcısı
  • koşul
  • yineleyici
  • vücut

... sonra döngü kabaca geçerli:

{ 
    initializer; 
    while (condition) 
    { 
     body; 
     iterator; 
    } 
} 

(iterator dışında bir t bir continue; ifade neticede de, elbette.)

başlatma bölümü mantıksal sadece bir kez olur, bu nedenle yalnızca bir "değişken örneklemesi" var ki tamamen mantıklı. Ayrıca, döngünün her yinelemesinde değişkenin doğal "başlangıç" değeri yoktur - for döngüsünün , başlatıcıda bir değişken bildiren, koşulda test eden ve değiştiren bir formda olduğunu söyleyecek hiçbir şey yoktur. yineleyicide. beklediğiniz Ne böyle bir döngü yapmak için:

for (int i = 0, j = 10; i < j; i++) 
{ 
    if (someCondition) 
    { 
     j++; 
    } 
    actions.Add(() => Console.WriteLine(i, j)); 
} 

karşılaştır Bu her yineleme için ayrı değişkeni bildirmek konum gibi görünüyor bir foreach döngü ile. Heck, değişken salt okunurdur, bu da yineleme arasında değişen bir değişken olduğunu düşünmek için daha daha garip yapar. foreach döngüsünü, yinelemeden aldığı değerle her bir yinelemede yeni bir salt okunur değişkeni ilan etmek olarak düşünmek mükemmel bir mantıklıdır.

+5

(Yüksek rütbeli bir geliştirici ile tartışmam benim için zor resminin yanında bir 559k ile, ancak :) Sağduyu (aklımın sınırları ile sınırlı olan kısım) katılmama eğilimindedir ve diğer birçok dilde, hem foreach hem de foreach gibi davranmak için görebilirsiniz. While döngüsünü şu şekilde yeniden yazalım: var x = initializer(); while (koşul) {feed_x_to_body (vücut); x = yineleyici(); } ve ben feed_x_to_body (ki bu derleyici için bir iştir) beklediğim gibi bir iş beklemekteyim (temeldeki uygulamaya göre mantıksal olandan değil - derleyicinin ilgisi bana değil!). Belki ben hatalıyım; daha fazla rehber lütfen. –

+0

@KavehShahbazian: Hayır, bu bir 'for' döngüsü çalışmıyor. Birçok 'for' döngüsünün * başlatıcıda tam olarak bir değişkenin bildirilmesi gerçeği alakasızdır. Başlatıcıda bildirilen değişken *, * döngüsünün gövdesinde kullanılan değişkendir - bu, gövdeye bir değişken * değeri * verilmemesidir. Özellikle, döngünün gövdesi * değişkeni de * değiştirebilir, bu da dünya modelinizi bozar. –

+0

"Değişkeni değiştirebilme", ​​bana açıkça gösterdi.Yine de yeni bir kapsam oluşturduğumda (lambda gövdesi gibi), değişkenin değeri ana alandan ayrılmalıdır. Bu bölüm hala aklımda bir yer bulamadı ve bu bölüm hakkında daha fazla rehberlik etmeniz çok iyi olurdu. Ama cevabınızı cevap olarak işaretliyorum çünkü (güncel) C# dünyasına uyuyor. Teşekkürler; –