2012-05-14 7 views
18

Dlaczego mój parametr x zachowuje się tak nieregularnie?Wyjaśnienie zakresu Lambda

  1. Przykład 1 - Nie istnieje w bieżącym kontekście.
  2. Przykład 2 - Nie można ponownie użyć x, ponieważ jest zdefiniowany w zakresie "podrzędnym".
  3. Przykład 3 - Dobrze. To jest ta część, w której jestem zdezorientowany. Może inny zakres "dziecka"?

Przykład 1:

List<int> list = new List<int> { 1, 2, 3, 4, 5 }; 
var result = list.Where(x => x < 3); 
Console.Write(result.ElementAt(x)); 

tworzy ten błąd czasu kompilacji:

The name 'x' does not exist in the current context

które oczekują.

Przykład 2:

List<int> list = new List<int> { 1, 2, 3, 4, 5 }; 
var result = list.Where(x => x < 3); 
int x = 1; 
Console.Write(result.ElementAt(x)); 

produkuje ten błąd kompilacji:

A local variable named 'x' cannot be declared in this scope because it would give a different meaning to 'x', which is already used in a 'child' scope to denote something else

Rozumiem scopingu jak odpowiedzieć na to pytanie, Is there a reason for C#'s reuse of the variable in a foreach?. Jest to jednak coś, czego nigdy wcześniej nie widziałem. Ponadto sprawia, że ​​odpowiedzi na to pytanie, What is the scope of a lambda variable in C#?, niekompletne lub błędne.

Przykład 3:

List<int> list = new List<int> { 1, 2, 3, 4, 5 }; 
List<string> stringList = new List<string> { "A", "B" }; 
var result = list.Where(x => x < 3); 
var result2 = stringList.Where(x => x != "A"); 

Console.Write(result2); 

żadne błędy wytwarzane.


Dzięki przyjętej odpowiedzi, te posty na blogu od Erica Lipperta pomogły mi objąć głowę tym, co się dzieje. Jeśli ktoś jest wciąż zamieszana

declaration space

simple names

+1

[tutaj] (http://blogs.msdn.com/b/ericlippert/archive/2009/11/02 /simple-names-are-not-so-simple.aspx) to dwa istotne [linki] (http://blogs.msdn.com/b/ericlippert/archive/2009/11/05/simple-names-are- nie tak prosty-część-dwa.aspx) na ten temat. – Servy

+0

możliwy duplikat [lokalnego zakresu zmiennych w anonimowej metodzie linq (zamknięcie)] (http://stackoverflow.com/questions/10517964/local-variable-scope-in-linq-anonymous-method-closure) – Magnus

+0

możliwy duplikat [ Jaki jest zakres zmiennej lambda w C#?] (Http://stackoverflow.com/questions/10494074/what-is-the-skope-of-a-ambambda-variable-in-c) – nawfal

Odpowiedz

16

W Example 1, X ma znaczenie określone w lokalnym zakresie wyrażenia lamdba i nie jest widoczny na trzeciej linii

W Example 2 teraz zadeklarowałeś dwie zmienne o nazwie "x" w tym samym zakresie deklaracji (widoczność jest inna)

Metoda lambda lub anonimowa "przechwytuje" zakres, w którym jest uruchomiony. Jeśli masz lokalny x w tym samym zakresie co definicja lambda, to "przechwytuje" to x, aby uzyskać dostęp do lambdy, co daje dwie definicje "x". To, co deklarujesz w lambda, nie jest przechwytywane w innym kierunku, więc nie jest widoczne poza lambdą.

W Example 3, Teraz nie używasz zmiennej, która jest lokalna tylko lambda poza lambdą, a nie nazywa czegoś tak samo w tym samym zakresie deklaracji.

+0

Wciąż nie " t get, jak/dlaczego przykład 2 nie kompiluje. Jeśli ex-2 nie będzie, to nawet ex-3 również nie powinno się skompilować. Zgodnie z moją logiką, druga deklaracja x "int x" nie powinna wywoływać żadnego zaniepokojenia z pierwszym, ponieważ jest jawnie zdefiniowana i dostępna tylko dla wyrażenia lambda zdefiniowanego w tej linii. To jest dla mnie cholernie nowe i dziwne. – MrClan

+1

@MrClan spójrz na [ten kod] (http://pastebin.com/DvDnNaPz) jest podobny do problemu z zakresu i może ci pomóc zrozumieć. –

+2

@MrClan nie kompiluje się, ponieważ "x => ..." deklaruje jedną zmienną x, a "int x = 1;" deklaruje inny. Lambda jest zamknięciem, które może wprowadzić "zmienne zewnętrzne", które są przechwytywane, gdy wykonywana jest lambda. Aby można było uchwycić te zmienne, muszą być unikatowe. "x" może oznaczać x zdefiniowane w lambda lub x zdefiniowane w zewnętrznym zakresie - więc kompilator nie wie, którego użyć w kodzie "x <3" ... –

4

Zasięg potomka (przykład 3) może korzystać z tych samych zmiennych, ale rodzic i dziecko nie mogą ponownie deklarować zmiennych.

można uzyskać to samo z dla:

// Child scopes 
for (int i = 1; i < 10; i++){ /* do something */ } 
for (int i = 1; i < 10; i++){ /* do something else */ } 

Byłoby to fail:

// Child and parent 
for (int i = 1; i < 10; i++){ /* do something */ } 
int i = 33; 
2

To nie jest tak skomplikowane jak się wydaje być.

Jeśli zdefiniować parametr dla wyrażenia lambda, parametr jest ważny tylko wewnątrz zakresu wyrażenia lambda

(int x) => 
{ 
    //x is only valid inside this scope 
} 

Jeśli masz drugą zmienną z definicją zawartą w takim samym zakresie jak wyrażenia lambda pojawi się błąd, ponieważ ta druga zmienna jest również poprawna w zakresie wyrażenia lamda.

void Foo() 
{ 

int y; 

//y is valis in the scope of foo 

(int x) => 
{ 
    //x is only valid inside this scope 
    //y is valid in the scope of foo and the lamda expression 
} 
} 

W trzecim przykładzie masz 2 różnych wyrażeń lambda, a zatem dwa różne zakresy