2013-05-28 23 views
11

Aşağıdaki kod çıktıları 012 yerine 33. Her bir yineleme döngüsünde neden aynı değişkeni yakalamak yerine yeni bir değişken loopScopedi'nin yakalanmadığını anlamıyorum.C# Lambda ifadeleri içinde değişkenleri bildirme

Action[] actions = new Action[3]; 

for (int i = 0; i < 3; i++) 
{ 

    actions [i] =() => {int loopScopedi = i; Console.Write (loopScopedi);}; 
} 

foreach (Action a in actions) a();  // 333 

Hopwever, bu kod 012 üretir. İkisi arasındaki fark nedir?

Action[] actions = new Action[3]; 

for (int i = 0; i < 3; i++) 
{ 
    int loopScopedi = i; 
    actions [i] =() => Console.Write (loopScopedi); 
} 

foreach (Action a in actions) a();  // 012 
+1

Ah bu değiştirilmiş bir kapatma sorunu - bkz http://stackoverflow.com/questions/235455/access-to-modified-closure –

+1

Kapatma sorunu: http://www.codethinked.com/c-closures-explained Örneğin. Davranış farklıdır. Net < 4.5 and > = 4.5 –

+2

Bu iki girişi Eric Lippert'den okuyun http://blogs.msdn.com/b/ericlippert/archive/2009/11/16/closing-over-the-loop-variable-part -two.aspx ve http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx – Icarus

cevap

7

olsun. Temel olarak, sadece bir tane i değişkeni vardır ve üç lambda buna atıfta bulunur. Sonunda, bir i değişkeni 3'a artırıldı, böylece üç eylemin tümü 3 yazdırır. (Daha sonra lambda çağrı kez lambda int loopScopedi = i sadece çalışır unutmayın.) İkinci versiyonda

, her yineleme için yeni int loopScopedi oluşturma ve 0 ve bir i geçerli değeri, onu ayarlıyorsunuz Her yineleme için 1 ve 2.

Sen ne olup bittiğini daha açık bir şekilde görmek için lambdas inlining hayal deneyebilirsiniz

:

foreach (Action a in actions) 
{ 
    int loopScopedi = i; // i == 3, since this is after the for loop 
    Console.Write(loopScopedi); // always outputs 3 
} 

Karşı: Bir lambda yakalanan

foreach (Action a in actions) 
{ 
    // normally you could not refer to loopScopedi here, but the lambda lets you 
    // you have "captured" a reference to the loopScopedi variables in the lambda 
    // there are three loopScopedis that each saved a different value of i 
    // at the time that it was allocated 
    Console.Write(loopScopedi); // outputs 0, 1, 2 
} 
2

ikisi arasındaki fark nedir?

Farklı kapsamı.

İlk döngünüzde, for döngü deyiminde tanımlanan i değişkenine atıfta bulunursunuz, ikinci döngüde ise yerel bir değişken kullanıyorsunuzdur. 333 çıktısı, ilk döngünüzün 3 kez yinelemesi ve buna bağlı olarak i değişkeninin en sonunda 3'e artırılması gerçeğinin bir sonucudur, daha sonra eylemleri çağırdığınızda, hepsi değişkenine (i) karşılık gelir. HerAction için yeni bir değişken kullandığınız ikinci döngüde

böylece bu "değiştirilmiş bir kapatma erişim" denir 012

2

Değişkenler lambda arasında paylaşılan bir sınıfa askıya alır ve dış kod.

İlk örneğinizde, i bir kez kaldırılır ve hem for() hem de tüm geçirilen lambdalarla birlikte kullanılır. Console.WriteLine'a ulaştığınızda, i, for()10 döngüsünden 3 ulaşmıştır.

İkinci örneğinizde, döngünün her bir çalışması için yeni bir loopScopedi kaldırılıyor, bu nedenle sonraki döngülerden etkilenmez.

2

Bu C# kapatmalarını nasıl ele alır. İlk örnekte kapak doğru şekilde yakalanmayacak ve her zaman son değeri kullanarak sonlanacaksınız; ancak ikinci örnekte, döngü değişkeninin geçerli değerini bir yer tutucusunda tutuyorsunuz ve sonra bu yer tutucuyu kullanıyorsunuz; Hangi doğru çözümü sağlar.

Ayrıca C# foreach döngülerindeki döngü değişkeni ile C# 5.0 ve önceki sürümlerdeki döngüler arasında nasıl bir değişiklik olduğunu fark eder.

(Hemen hemen) aynı soruyu aldım ve bunun hakkında bir şeyler öğrendim here.