Étant donné que personne ici n'a cité directement ECMA-334 :
10.4.4.10 Pour les relevés
Affectation définitive vérifiant une instruction for du formulaire:
for (for-initializer; for-condition; for-iterator) embedded-statement
se fait comme si la déclaration était écrite:
{
for-initializer;
while (for-condition) {
embedded-statement;
LLoop: for-iterator;
}
}
Plus loin dans la spécification,
12.16.6.3 Instanciation des variables locales
Une variable locale est considérée comme instanciée lorsque l'exécution entre dans la portée de la variable.
[Exemple: Par exemple, lorsque la méthode suivante est invoquée, la variable locale x
est instanciée et initialisée trois fois, une fois pour chaque itération de la boucle.
static void F() {
for (int i = 0; i < 3; i++) {
int x = i * 2 + 1;
...
}
}
Cependant, déplacer la déclaration de l' x
extérieur de la boucle se traduit par une seule instanciation de x
:
static void F() {
int x;
for (int i = 0; i < 3; i++) {
x = i * 2 + 1;
...
}
}
exemple de fin]
Lorsqu'elles ne sont pas capturées, il n'y a aucun moyen d'observer exactement à quelle fréquence une variable locale est instanciée - car les durées de vie des instanciations sont disjointes, il est possible pour chaque instanciation d'utiliser simplement le même emplacement de stockage. Cependant, lorsqu'une fonction anonyme capture une variable locale, les effets de l'instanciation deviennent apparents.
[Exemple: l'exemple
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();
}
}
produit la sortie:
1
3
5
Cependant, lorsque la déclaration de x
est déplacée hors de la boucle:
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 sortie est:
5
5
5
Notez que le compilateur est autorisé (mais pas obligatoire) pour optimiser les trois instanciations en une seule instance déléguée (§11.7.2).
Si une boucle for déclare une variable d'itération, cette variable elle-même est considérée comme déclarée en dehors de la boucle. [Exemple: Ainsi, si l'exemple est modifié pour capturer la variable d'itération elle-même:
static D[] F() {
D[] result = new D[3];
for (int i = 0; i < 3; i++) {
result[i] = () => { Console.WriteLine(i); };
}
return result;
}
une seule instance de la variable d'itération est capturée, ce qui produit la sortie:
3
3
3
exemple de fin]
Oh oui, je suppose qu'il convient de mentionner qu'en C ++, ce problème ne se produit pas car vous pouvez choisir si la variable est capturée par valeur ou par référence (voir: Capture Lambda ).