Dies hat nichts mit Schleifen zu tun.
Dieses Verhalten wird ausgelöst, weil Sie einen Lambda-Ausdruck verwenden, () => variable * 2bei dem der äußere Bereich variablenicht tatsächlich im inneren Bereich des Lambda definiert ist.
Lambda-Ausdrücke (in C # 3 + sowie anonyme Methoden in C # 2) erstellen weiterhin tatsächliche Methoden. Das Übergeben von Variablen an diese Methoden ist mit einigen Dilemmata verbunden (Übergeben von Wert? Übergeben von Referenz? C # geht mit Referenz einher - dies eröffnet jedoch ein weiteres Problem, bei dem die Referenz die tatsächliche Variable überleben kann). Um all diese Dilemmata zu lösen, erstellt C # eine neue Hilfsklasse ("Closure") mit Feldern, die den in den Lambda-Ausdrücken verwendeten lokalen Variablen entsprechen, und Methoden, die den tatsächlichen Lambda-Methoden entsprechen. Alle Änderungen an variableIhrem Code werden tatsächlich übersetzt, um dies zu ändernClosureClass.variable
Ihre while-Schleife wird also so lange aktualisiert, ClosureClass.variablebis sie 10 erreicht. Dann führen Sie for-Schleifen die Aktionen aus, die alle auf derselben Weise ausgeführt werden ClosureClass.variable.
Um das erwartete Ergebnis zu erzielen, müssen Sie eine Trennung zwischen der Schleifenvariablen und der Variablen, die geschlossen wird, erstellen. Sie können dies tun, indem Sie eine andere Variable einführen, dh:
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
var t = variable; // now t will be closured (i.e. replaced by a field in the new class)
actions.Add(() => t * 2);
++variable; // changing variable won't affect the closured variable t
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
Sie können den Verschluss auch auf eine andere Methode verschieben, um diese Trennung zu erstellen:
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
actions.Add(Mult(variable));
++variable;
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
Sie können Mult als Lambda-Ausdruck implementieren (impliziter Abschluss).
static Func<int> Mult(int i)
{
return () => i * 2;
}
oder mit einer tatsächlichen Helferklasse:
public class Helper
{
public int _i;
public Helper(int i)
{
_i = i;
}
public int Method()
{
return _i * 2;
}
}
static Func<int> Mult(int i)
{
Helper help = new Helper(i);
return help.Method;
}
In jedem Fall handelt es sich bei "Closures" NICHT um ein Konzept, das sich auf Schleifen bezieht , sondern auf anonyme Methoden / Lambda-Ausdrücke, bei denen Variablen mit lokalem Gültigkeitsbereich verwendet werden - obwohl einige vorsichtige Verwendung von Schleifen Schließfallen aufzeigen.