Como aquí nadie citó directamente a ECMA-334 :
10.4.4.10 Para declaraciones
Comprobación de asignación definitiva para una declaración for del formulario:
for (for-initializer; for-condition; for-iterator) embedded-statement
se hace como si la declaración estuviera escrita:
{
for-initializer;
while (for-condition) {
embedded-statement;
LLoop: for-iterator;
}
}
Más adelante en la especificación,
12.16.6.3 Instanciación de variables locales
Una variable local se considera instanciada cuando la ejecución entra en el alcance de la variable.
[Ejemplo: por ejemplo, cuando se invoca el siguiente método, la variable local x
se instancia e inicializa tres veces, una para cada iteración del bucle.
static void F() {
for (int i = 0; i < 3; i++) {
int x = i * 2 + 1;
...
}
}
Sin embargo, mover la declaración de x
fuera del ciclo da como resultado una sola instanciación de x
:
static void F() {
int x;
for (int i = 0; i < 3; i++) {
x = i * 2 + 1;
...
}
}
ejemplo final]
Cuando no se captura, no hay forma de observar exactamente con qué frecuencia se instancia una variable local, ya que las vidas de las instancias son disjuntas, es posible que cada instancia simplemente use la misma ubicación de almacenamiento. Sin embargo, cuando una función anónima captura una variable local, los efectos de la instanciación se hacen evidentes.
[Ejemplo: el ejemplo
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();
}
}
produce la salida:
1
3
5
Sin embargo, cuando la declaración de x
se mueve fuera del bucle:
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;
}
la salida es:
5
5
5
Tenga en cuenta que el compilador está permitido (pero no es obligatorio) para optimizar las tres instancias en una sola instancia de delegado (§11.7.2).
Si un ciclo for declara una variable de iteración, se considera que esa variable se declara fuera del ciclo. [Ejemplo: por lo tanto, si se cambia el ejemplo para capturar la variable de iteración en sí:
static D[] F() {
D[] result = new D[3];
for (int i = 0; i < 3; i++) {
result[i] = () => { Console.WriteLine(i); };
}
return result;
}
solo se captura una instancia de la variable de iteración, que produce la salida:
3
3
3
ejemplo final]
Ah sí, supongo que debería mencionarse que en C ++ este problema no ocurre porque puedes elegir si la variable se captura por valor o por referencia (ver: captura de Lambda ).