2009-11-02 16 views
7

Aşağıdaki programda, DummyMethod her zaman yazdırır 5. Ancak, yorumlanan kodu kullanırsak, farklı değerler alırız (yani 1, 2, 3, 4). Herkes bunun neden olduğunu açıklayabilir mi?C# delikleri ile sorun #

 delegate int Methodx(object obj); 

     static int DummyMethod(int i) 
     { 
      Console.WriteLine("In DummyMethod method i = " + i); 
      return i + 10; 
     } 

     static void Main(string[] args) 
     {  
      List<Methodx> methods = new List<Methodx>(); 

      for (int i = 0; i < 5; ++i) 
      { 
       methods.Add(delegate(object obj) { return DummyMethod(i); }); 
      } 

      //methods.Add(delegate(object obj) { return DummyMethod(1); }); 
      //methods.Add(delegate(object obj) { return DummyMethod(2); }); 
      //methods.Add(delegate(object obj) { return DummyMethod(3); }); 
      //methods.Add(delegate(object obj) { return DummyMethod(4); }); 

      foreach (var method in methods) 
      { 
       int c = method(null); 
       Console.WriteLine("In main method c = " + c); 
      } 
     } 

Ayrıca aşağıdaki kod kullanılırsa, istenen sonucu alırım.

 for (int i = 0; i < 5; ++i) 
     { 
      int j = i; 
      methods.Add(delegate(object obj) { return DummyMethod(j); }); 
     } 
+2

Bu soruyu favorilere ekleyin. Bu tür davranışlar sadece "tehlikeli" hisseder. Bahse girerim C# gibi daha da zor hatalardan biri olarak daha sık ortaya çıkacak. Bu, anahtar cümlelerin döngüsel eşdeğerinin, zorunlu "kesinti" olmaksızın aşağı doğru kademeli olarak hareket etmesine izin verilmesi gibidir. cümlesi –

+0

@Neil: Kabul ediyorum, bu bir gotcha-hotspot. "Foreach" ile görüldüğünde daha kafa karıştırıcı oluyor - öyle ki C# takımı bu olayın davranışını değiştirmeyi düşünüyor. (Foreach davranışının arzu edilen bir senaryo olduğunu düşünmek zordur; değişken için sadece bir kez bildirilmiş olduğu için döngü durumu daha anlaşılırdır.) –

+0

[C# Yakalanan Değişken Döngüde] olası bir kopyası (http: //stackoverflow.com/questions/271440/c-sharp-captured-variable-in-loop) – nawfal

cevap

17

sorun her temsilci aynı değişken i yakalayan konum olmasıdır - sadece Yerine değeri 5.

sahiptir döngünün sonunda hangi, her temsilci farklı yakalamak istediğiniz döngüde yeni bir değişken bildirerek demektir değişken,:

for (int i = 0; i < 5; ++i) 
{ 
    int localCopy = i; 
    methods.Add(delegate(object obj) { return DummyMethod(localCopy); }); 
} 

Bu oldukça yaygın bir "yakaladım" - Eğer my closures article yakalanan değişkenleri ve kapanışları hakkında biraz daha okuyabilir.

+1

Jon, kafamı neden bulamaya başladığını anlamaya çalışıyorum. "Ben" diye bir değer türü düşüncesi olarak baktım, "bu bir kopya olarak geçilmesi gerekiyor", bu yüzden nasıl referans benzeri bir davranışa sahip göründüğünü göremedim ... Bir özet/bir liner var. beni doğru yöne yönlendir? –

+2

Son bağlantıya bir göz atın. Başınızı döndürecek şey, değişkenin yakalanan * değeri * değil, değişkenin kendisidir. –

+1

Bunu düşünün: Yöntemde o noktaya doğru çağrılan küçük bir satır içi olay olduğu gibi, tüm değişkenlere ve duruma erişime sahip olduğu gibi, yalnızca yöntemin oluşturulmadığı zamanlarda değişkenleri ve durumu elde edersiniz. Böylece, anonim bir yöntem yaptığınızda, yönteme çağrıldığında basitçe erişirsiniz. – RCIX

2

Ben değişken i yığınına alınır çünkü düşünüyorum (değişken yakalanan bir var)

this answer'a bakın. Eğer farkı görebilirsiniz (yansıtıcı kullanarak) oluşturulan koduna baktığımızda

+1

"Yakalanan değişken" in daha kullanışlı bir terminoloji olduğunu düşünüyorum - tam olarak, * değeri * yerine yakalanan * değişken * olması. –

+0

@Jon: Mantıklı geliyor, onu düzeliyorum –

4

: Eğer arka planda geçici bir sınıf oluşturur başlangıç ​​kodunu kullandığınızda

private static void Method2() 
{ 
    List<Methodx> list = new List<Methodx>(); 
    Methodx item = null; 
    <>c__DisplayClassa classa = new <>c__DisplayClassa(); 
    classa.i = 0; 
    while (classa.i < 5) 
    { 
     if (item == null) 
     { 
      item = new Methodx(classa.<Method2>b__8); 
     } 
     list.Add(item); 
     classa.i++; 
    } 
    foreach (Methodx methodx2 in list) 
    { 
     Console.WriteLine("In main method c = " + methodx2(null)); 
    } 
} 

, bu sınıf bir başvuru tutan "i" değişkeni, Jon'un cevabına göre, sadece bunun son değerini görürsünüz.

private sealed class <>c__DisplayClassa 
{ 
    // Fields 
    public int i; 

    // Methods 
    public <>c__DisplayClassa(); 
    public int <Method2>b__8(object obj); 
} 

Gerçekten ne olup bittiğini görmek için Reflector kod bakarak tavsiye onun ben yakalanan değişkenlerin mantıklı nasıl. Option (Kod) 'ın Option (Optimizasyon) seçeneğini Option (Seçenek) menüsünde ".NET 1.0"' a ayarladığınızdan emin olun, aksi takdirde tüm arka sahneleri gizlersiniz.