2014-05-07 19 views
34

Önceden özür dilememe izin verin - muhtemelen terminolojiyi topluyorum. Kapanmanın ne olduğuna dair belirsiz bir anlayışa sahibim, ancak gördüğüm davranışları açıklayamıyorum. En azından, bunun bir kapatma sorunu olduğunu düşünüyorum. Çevrimiçi arama yaptım, ancak istediğimi almak için doğru anahtar kelimeleri bulamadım.Neden Bazı Kapaklar 'Diğerleri' Friendlier '?

Özellikle - BENİM BENZER BENİM (en azından gözlerim) olan iki kod kodum var. Birincisi:

static void Main(string[] args) 
{ 
    Action x1 = GetWorker(0); 
    Action x2 = GetWorker(1); 
} 

static Action GetWorker(int k) 
{ 
    int count = 0; 

    // Each Action delegate has it's own 'captured' count variable 
    return k == 0 ? (Action)(() => Console.WriteLine("Working 1 - {0}",count++)) 
        : (Action)(() => Console.WriteLine("Working 2 - {0}",count++)); 
} 

bu kodu çalıştırmak ve x1() ve x2 çağırmak durumunda() onlar ayrı bir 'sayım' değerini korumak olduğunu göreceksiniz.

foreach(var i in Enumerable.Range(0,4)) 
    { 
     x1(); x2(); 
    } 

Çıkışlar:

bana mantıklı ve okudum açıklamalar maçları
Working 1 - 0 
Working 2 - 0 
Working 1 - 1 
Working 2 - 1 
Working 1 - 2 
Working 2 - 2 
Working 1 - 3 
Working 2 - 3 

. Sahnelerin arkasında her delege/eylem için bir sınıf yaratılır ve sınıf 'sayım' değerini tutmak için bir alan verilir. Akıllı hissederek yatağa gittim!

AMA SONRA - Bu çok benzer bir kod çalıştı:

// x3 and x4 *share* the same 'captured' count variable 
    Action x3 =() => Console.WriteLine("Working 3 - {0}", count++); 
    Action x4 =() => Console.WriteLine("Working 4 - {0}", count++); 

Ve (comment diyor gibi) davranış burada tamamen farklıdır. x3() ve x4() SAME sayım değerine sahip gibi görünüyor!

Working 3 - 0 
Working 4 - 1 
Working 3 - 2 
Working 4 - 3 
Working 3 - 4 
Working 4 - 5 
Working 3 - 6 
Working 4 - 7 

Ben neler görebilirsiniz - ama onlar farklı olarak ele alınır Gerçekten neden alamadım. Kafamda - gördüğüm özgün davranışı beğendim, ama sonradan örnek beni şaşırtıyor. Umarım bu mantıklıdır. Teşekkürler

+27

İlk örneğinizde iki farklı "int count" değişken bildirimi vardı (ayrı yöntem çağrılarından).İkinci örneğiniz _same_ değişken bildirimini paylaşıyor. İlk örneğiniz, ikinci örnekteki "int say" ın ana programınızın alanı olduğu ile aynı davranacaktır. –

cevap

50

İlk örneğiniz iki farklı int count değişken bildirimine sahipti (ayrı yöntem çağrılarından). İkinci örneğiniz değişken bildirimini aynı paylaşıyor.

static int count = 0; 

static Action GetWorker(int k) 
{ 
    return k == 0 ? (Action)(() => Console.WriteLine("Working 1 - {0}",count++)) 
        : (Action)(() => Console.WriteLine("Working 2 - {0}",count++)); 
} 

Bu çıkışlar:

İlk örnek ikinci örnekle aynı davranırdı int count ana programın bir alan olmuştu

Working 1 - 0 
Working 2 - 1 
Working 1 - 2 
Working 2 - 3 
Working 1 - 4 
Working 2 - 5 
Working 1 - 6 
Working 2 - 7 

Sen üçlü operatör olmadan kolaylaştırabilirsiniz ayrıca:

static Action GetWorker(int k) 
{ 
    int count = 0; 

    return (Action)(() => Console.WriteLine("Working {0} - {1}",k,count++)); 
} 

Hangi çıkışlar:

Working 1 - 0 
Working 2 - 0 
Working 1 - 1 
Working 2 - 1 
Working 1 - 2 
Working 2 - 2 
Working 1 - 3 
Working 2 - 3 

ana konu (sizin durumunuzda int count = 0; olarak) bir yöntem bildirilen bir yerel değişken yöntemin çağırma için benzersizdir lambda temsilci oluşturulduğunda, o zaman, her biri uygulayarak olmasıdır kendine özgü count değişken etrafında kapatma:

Action x1 = GetWorker(0); //gets a count 
Action x2 = GetWorker(1); //gets a new, different count 
6

Buradaki fark, bir örnekte bir delege, diğeri iki var.

Sayım değişkeni yerel olduğu için, her arama yaptığınızda yeniden oluşturulur. Yalnızca bir delege kullanıldığı için (üçlüden dolayı) her delege değişkenin farklı bir kopyasını alır. Diğer örnekte, her iki temsilci de aynı değişkeni alır.

Üçlü operatör yalnızca iki bağımsız değişkeninden birini döndürür, bu nedenle kapanış beklediğiniz gibi çalışır. İkinci örnekte, aynı "ana" sayım değişkeni paylaşan ve farklı sonuç veren iki kapak oluşturursunuz. Açıkçası burada üretilen tek kapatma var

static Action GetWorker(int k) 
{ 
    int count = 0; 
    Action returnDelegate 

    // Each Action delegate has it's own 'captured' count variable 
    if (k == 0) 
     returnDelegate = (Action)(() => Console.WriteLine("Working 1 - {0}",count++)); 
    else 
     returnDelegate = (Action)(() => Console.WriteLine("Working 2 - {0}",count++)); 

    return returnDelegate 
} 

ve diğer numune besbelli vardır:

sen bir de şu yönden bakarsak Biraz daha net olabilir

(Bu ilk numuneye eşdeğer kodudur) iki.

+4

Eh, üçüncül operatörün onunla bir ilgisi olduğunu sanmıyorum; İki yerel değişken oluşturmanın iki yöntem çağrıdır. Üçlü operatörünü kaldırırsanız, aynı sonucu elde edersiniz (her seferinde “Çalışma 1” diyecek) fakat “sayım” davranışı aynı olacaktır. –

+0

@ ChrisSinclair, bu doğru. Bu karışıklık, * iki delegenin de olduğu gibi * göründüğü gibi dönüyor gibi görünüyor. Bu bilgiyi içerecek şekilde güncellemeyi teşekkürler! – BradleyDotNET

27

bir kapatılması değişken yakalar.

bir bir yöntemadı varlık tarafından aktive olduğu zaman yerel değişken oluşturulur. (Yerel değişkenler yaratan başka şeyler var ama şimdilik bunu görmezden gelelim.)

İlk örneğinizde, GetWorker iki aktivasyonunuz var ve bu nedenle count adında iki tamamen bağımsız değişken oluşturuldu. Her biri bağımsız olarak yakalanır.

Maalesef, hepsini göstermediğiniz ikinci örneğinizde, tek bir aktivasyonunuz ve iki kapanışınız var. Kapılar değişkeni paylaşır. İşte

yardımcı olabileceğini düşünmek için bir yol var:

class Counter { public int count; } 
... 
Counter Example1() 
{ 
    return new Counter(); 
} 
... 
Counter c1 = Example1(); 
Counter c2 = Example1(); 
c1.count += 1; 
c2.count += 2; 
// c1.count and c2.count are different. 

Vs

void Example2() 
{ 
    Counter c = new Counter(); 
    Counter x3 = c; 
    Counter x4 = c; 
    x3.count += 1; 
    x4.count += 2; 
    // x3.count and x4.count are the same. 
} 

ilk örnekte count denilen iki değişken olmadığını vardır neden sizin için mantıklı mı Birden çok nesne tarafından paylaşılırken, ikincisinde birden çok nesne tarafından paylaşılan yalnızca bir tane var mı?

+0

"Yerel değişkenleri yaratan başka şeyler var ama şimdilik bunu göz ardı edelim." Buna MSIL perspektifinden bakıyorum, yani başka bir yerel değişken oluşturabilir? – riki

+2

@Felheart: Kesin bir şekilde konuşuyordum; daha ziyade yerel değişkenler olmayan, ancak yakalanabilen program öğeleri olduğunu söylemeliydim. Geçtiğimiz değer resmi parametreler, yakalanabilecek değişkenlerdir. Ayrıca, "bu" değeri, bir değer-arası değişkeni değil, yakalanabilir. Ayrıca bir anlamda, bir genel yöntem bir lambda içerdiğinde tip argümanı yakalanır ve bu herhangi bir değişken değildir. –

2

diğer alternatif (belki ne aradığını):

static Action<int> GetWorker() 
{ 
    int count = 0; 

    return k => k == 0 ? 
      Console.WriteLine("Working 1 - {0}",count++) : 
      Console.WriteLine("Working 2 - {0}",count++); 
} 

Ardından:

var x = GetWorker(); 

foreach(var i in Enumerable.Range(0,4)) 
{ 
    x(0); x(1); 
}  

Ya da belki: Ya da belki bazı köri ile

var y = GetWorker(); 
// and now we refer to the same closure 
Action x1 =() => y(0); 
Action x2 =() => y(1); 

foreach(var i in Enumerable.Range(0,4)) 
{ 
    x1(); x2(); 
} 

:

var f = GetWorker(); 
Func<int, Action> GetSameWorker = k =>() => f(k); 

// k =>() => GetWorker(k) will not work 

Action z1 = GetSameWorker(0); 
Action z2 = GetSameWorker(1);  

foreach(var i in Enumerable.Range(0,4)) 
{ 
    z1(); z2(); 
} 
İlgili konular