Ponieważ nikt tutaj bezpośrednio nie cytował ECMA-334 :
10.4.4.10 Dla wyciągów
Określone sprawdzanie przypisania dla oświadczenia formularza:
for (for-initializer; for-condition; for-iterator) embedded-statement
odbywa się tak, jakby napisano oświadczenie:
{
for-initializer;
while (for-condition) {
embedded-statement;
LLoop: for-iterator;
}
}
W dalszej części specyfikacji
12.16.6.3 Tworzenie instancji zmiennych lokalnych
Zmienna lokalna jest uważana za utworzoną, gdy wykonanie wchodzi w zakres zmiennej.
[Przykład: Na przykład, gdy wywoływana jest następująca metoda, zmienna lokalna x
jest tworzona i inicjowana trzykrotnie - raz dla każdej iteracji pętli.
static void F() {
for (int i = 0; i < 3; i++) {
int x = i * 2 + 1;
...
}
}
Jednak przeniesienie deklaracji x
poza pętlę powoduje pojedyncze utworzenie x
:
static void F() {
int x;
for (int i = 0; i < 3; i++) {
x = i * 2 + 1;
...
}
}
przykład końca]
Jeśli nie zostanie przechwycony, nie można dokładnie obserwować, jak często tworzona jest instancja zmiennej lokalnej - ponieważ czasy życia instancji są rozłączne, możliwe jest, aby każda instancja po prostu używała tego samego miejsca przechowywania. Jednak gdy anonimowa funkcja przechwytuje zmienną lokalną, efekty tworzenia instancji stają się widoczne.
[Przykład: przykład
using System;
delegate void D();
class Test{
static D[] F() {
D[] result = new D[3];
for (int i = 0; i < 3; i++) {
int x = i * 2 + 1;
result[i] = () => { Console.WriteLine(x); };
}
return result;
}
static void Main() {
foreach (D d in F()) d();
}
}
daje wynik:
1
3
5
Jednak gdy deklaracja x
jest przenoszona poza pętlę:
static D[] F() {
D[] result = new D[3];
int x;
for (int i = 0; i < 3; i++) {
x = i * 2 + 1;
result[i] = () => { Console.WriteLine(x); };
}
return result;
}
dane wyjściowe to:
5
5
5
Należy pamiętać, że kompilator jest dozwolony (ale nie wymagany) do optymalizacji trzech instancji w pojedynczej instancji delegata (§ 11.7.2).
Jeśli pętla for deklaruje zmienną iteracyjną, sama zmienna jest uważana za zadeklarowaną poza pętlą. [Przykład: Jeśli więc przykład zostanie zmieniony w celu przechwycenia samej zmiennej iteracyjnej:
static D[] F() {
D[] result = new D[3];
for (int i = 0; i < 3; i++) {
result[i] = () => { Console.WriteLine(i); };
}
return result;
}
przechwytywana jest tylko jedna instancja zmiennej iteracyjnej, która daje wynik:
3
3
3
przykład końca]
O tak, myślę, że należy wspomnieć, że w C ++ ten problem nie występuje, ponieważ możesz wybrać, czy zmienna jest przechwytywana przez wartość, czy przez referencję (patrz: Przechwytywanie lambda ).