2010-08-05 16 views
45

Dışsal Değişken Tuzak Tam olarak nedir? Açıklama ve C# örnekleri takdir edilmektedir.Dış Değişken Tuzak

DÜZENLEME: Jon Skeet en diktayı :) birleştirilmesi

Eric Lippert on the Outer Variable Trap

+3

Ben googled kadar ne hakkında konuştuğunu hiçbir fikrim yoktu; Bunu yaparken tonlarca açıklama ve örnek buldum (C#), başka ne arıyorsun? – Marc

+2

@Marc Belki OP bu kişilerden biridir (en azından kesin bir şekilde birçok kez söylenir), SO'nun her olası ilgili programlama sorusu için bir yanıtı olmasını ister. Bunun cevabı görünüşte eksikti. –

+1

@Maciej, Mükemmel. Ana liste tamamlanmaya bir adım daha yakın! Web egemenliği, işte geliyoruz! – Marc

cevap

61

bir geliştirici bir değişkenin değeri aslında bir lambda ifadesi veya anonim temsilci tarafından yakalanabilir umduğu "Dış Değişken Tuzak" oluşur değişken kendini yakalar.

Örnek:

var actions = new List<Action>(); 
for (var i = 0; i < 10; i++) 
{ 
    actions.Add(() => Console.Write("{0} ", i)); 
} 
foreach (var action in actions) 
{ 
    action(); 
} 

olası çıkış # 1:

0 1 2 3 4 5 6 7 8 9 

olası çıkış # 2:

10 10 10 10 10 10 10 10 10 10 

halinde 1 numaralı çıkışı beklediniz, Dış Değişken Tuzağına düştünüz. 2 numaralı çıkışı aldın.

Fix:

bir "İç Değişken" Declare defalarca yerine sadece bir kez yakalanır "Dış Değişken" nin çekilecek.

Daha fazla ayrıntı için
var actions = new List<Action>(); 
for (var i = 0; i < 10; i++) 
{ 
    var j = i; 
    actions.Add(() => Console.Write("{0} ", j)); 
} 
foreach (var action in actions) 
{ 
    action(); 
} 

, ayrıca Eric Lippert's blog görüyoruz.

foreach (var s in strings) 
    var x = results.Where(r => (r.Text).Contains(s)); 

gibi

+1

"Tuzak" kelimesi üzerinde ilginç bir kelime: Değişken yakalandı (tuzağa düştün) ve bir sorunla yakalandınız (bir tuzağa düştünüz) –

+4

Bunun kabul edilen cevap olması muhtemel olduğu göz önünde bulundurulduğunda, Eric Lippert'in blog gönderisine bir bağlantı eklemeniz mümkün mü? http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx –

+0

Yani j temelde 'taze' bir değişkendir ve bu nedenle k-th eylemi, loop değişkeninin varsaydığı k-th değerine bağlı bir j_k değişkenini içerir. Sonuç olarak beklenen davranış elde edilir. –

4

şey her yineleme için yürütülmez içerir çünkü bekliyorsanız sonuçlar verir olmayacak. Döngü içinde geçici bir değişkene atama, bunu düzeltecektir.

+0

'var =' sözdizimine aşina değilim, ne işe yarar? = P – Marc

+0

Nitpick: 'İçerir 'her iterasyon için yürütülür, ancak' s' her zaman aynı değere sahip olacaktır. – dtb

+0

@dtb @Marc Evet, dtb ne dedi. Muhtemelen bu kadar iyi açıklamam. Var sadece dönüş türünü ilan etmek için bir alternatiftir (gerçek kodda aslında buraya dönüş tipini bildirirdim ama bir IDE'nin önünde değilim ve neyin döndüğünü neye benzediğini hissetmedim ... Sanırım onun IEnumberable <>) – heisenberg

1

@dtb doğru (büyük +1), ancak bunun yalnızca kapağın kapsamı döngü dışında kaldığında geçerli olduğunu unutmayın. Örneğin:

var objects = new [] 
    { 
     new { Name = "Bill", Id = 1 }, 
     new { Name = "Bob", Id = 5 }, 
     new { Name = "David", Id = 9 } 
    }; 

for (var i = 0; i < 10; i++) 
{ 
    var match = objects.SingleOrDefault(x => x.Id == i); 

    if (match != null) 
    { 
     Console.WriteLine("i: {0} match: {1}", i, match.Name); 
    } 
} 

Bu yazdırır:

i: 1 match: Bill 
i: 5 match: Bob 
i: 9 match: David

ReSharper güvenle bu durumda göz ardı edilebilir "modifiye kapatılması, Erişim" konusunda uyaracaktır.

0

Bu tuzak şimdi kapatılacak döngü değişkeninin yeni bir kopya her zaman üzerinde foreach döngüler kapanışları içeride yani C# 5.0 beri çok ama has been changedforeach döngüler için var olduğunu not etmek layık. Yani aşağıda kodu:

var values = new List<int>() { 100, 110, 120 }; 
var funcs = new List<Func<int>>(); 
foreach (var v in values) 
    funcs.Add(() => v); 
foreach (var f in funcs) 
    Console.WriteLine(f()); 

Baskılar 120 120 120< C# 5.0 ancak

100 110 120> = C# 5.0 Ancak for döngüler hala aynı şekilde davranır.