Dies hat nichts mit Schleifen zu tun.
Dieses Verhalten wird ausgelöst, weil Sie einen Lambda-Ausdruck verwenden, () => variable * 2
bei dem der äußere Bereich variable
nicht 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 variable
Ihrem Code werden tatsächlich übersetzt, um dies zu ändernClosureClass.variable
Ihre while-Schleife wird also so lange aktualisiert, ClosureClass.variable
bis 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.