Gibt es einen Grund für C#'s wiederverwenden der Variablen in einer foreach?
Bei der Verwendung von lambda-Ausdrücke oder anonyme Methoden in C#, wir müssen vorsichtig sein, die Zugang zu modifizierten Verschluss Falle. Zum Beispiel:
foreach (var s in strings)
{
query = query.Where(i => i.Prop == s); //access to modified closure
...
}
Durch die veränderte Schließung der obige code bewirkt, dass alle von der Where
Klauseln, die auf der Abfrage basieren auf den endgültigen Wert von s
.
Erläutert hier, dies geschieht, weil die s
variable deklariert foreach
Schleife oben übersetzt wird, wie dies in den compiler:
string s;
while (enumerator.MoveNext())
{
s = enumerator.Current;
...
}
statt so:
while (enumerator.MoveNext())
{
string s;
s = enumerator.Current;
...
}
Als darauf hingewiesen,hier, gibt es keine performance-Vorteile zu deklarieren einer Variablen außerhalb der Schleife, und unter normalen Umständen der einzige Grund, der mir einfällt, dies zu tun ist, wenn Sie planen, verwenden Sie die variable außerhalb der Schleife:
string s;
while (enumerator.MoveNext())
{
s = enumerator.Current;
...
}
var finalString = s;
Jedoch Variablen definiert foreach
Schleife kann nicht verwendet werden, außerhalb der Schleife:
foreach(string s in strings)
{
}
var finalString = s; //won't work: you're outside the scope.
Damit der compiler deklariert die variable in einer Weise, die macht es sehr anfällig für Fehler, die oft schwer zu finden und zu Debuggen, während der Produktion keine wahrnehmbaren Vorteile.
Gibt es etwas, was Sie tun können, mit foreach
Schleifen diese Weise, dass Sie konnten nicht, wenn Sie kompiliert wurden, mit einem inneren Gültigkeitsbereich variable, oder ist dies nur eine willkürliche Wahl, die vorgenommen wurde, bevor die anonyme Methoden und lambda-Ausdrücke zur Verfügung standen oder gemeinsam, und die bisher nicht überarbeitet, da Sie dann?
String s; foreach (s in strings) { ... }
?die OP ist wirklich nicht sprechen
foreach
aber über lamda-Ausdrücke resultieren in ähnlichen code wie gezeigt, durch die OP...Nicht, dass kompilieren? (Fehler: Typ und id sind beide erforderlich, in einer foreach-Anweisung für mich)
Es ist ein geschlossen-über äußere lokalen einer lambda; warum bist du davon aus, dass es sich dabei auf dem stack? Es ist Leben, ist länger als der stack-frame!
Ich bin verwirrt. Ich verstehe, dass lambda fängt einen Verweis auf die foreach-variable (was ist intern, erklärt außerhalb die Schleife) und damit Sie am Ende den Vergleich gegen seinen endgültigen Wert; ich bekomme. Was ich nicht verstehe ist, wie man das deklarieren der variable inside die Schleife wird überhaupt einen Unterschied. Aus einer compiler-writer Sicht ich bin nur die Zuweisung einer string-Referenz (var 's') auf dem Stapel, unabhängig davon, ob die Erklärung innerhalb oder außerhalb der Schleife; ich würde sicherlich nicht wollen, schieben Sie eine neue Referenz auf den stack jeder iteration!
InformationsquelleAutor StriplingWarrior | 2012-01-17
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ihre Kritik ist völlig gerechtfertigt.
Ich Diskutiere dieses problem im detail hier:
Schließen über den loop-variable considered harmful
Letztere. Die C# 1.0-Spezifikation eigentlich nicht sagen, ob die loop-variable war der innerhalb oder außerhalb der Schleife, da machte es keinen beobachtbaren Unterschied. Beim closure-Semantik eingeführt wurden, die in C# 2.0, die Wahl wurde gemacht, um die loop-variable außerhalb der Schleife, im Einklang mit der "for" - Schleife.
Ich denke, es ist fair zu sagen, dass alle diese Entscheidung bereuen. Dies ist eine der schlimmsten, die "Fallstricke" in C#, und wir gehen, um den Bruch zu ändern, um es zu beheben. In C# 5 die foreach-Schleife die variable logisch innen der Körper der Schleife, und deshalb Verschlüsse bekommen Sie eine neue Kopie jedes mal.
Den
for
Schleife nicht geändert werden und die änderung wird nicht sein "zurück portiert" zu früheren Versionen von C#. Daher sollten Sie weiterhin vorsichtig sein bei der Verwendung dieser Redewendung.Und jetzt müssen wir erinnern
foreach
"sicher" ist aberfor
ist nicht.Die änderung zu brechen, in dem Sinne, dass es nicht abwärtskompatibel ist. Der neue code wird nicht korrekt ausgeführt, wenn es mit einem älteren compiler.
Nein, das ist der Grund, warum wir bereit sind, es zu nehmen. Jon Skeet wies mich darauf hin, eine wichtige breaking-ändern-Szenario aber, das ist, dass jemand schreibt code in C# 5, testet es und dann teilt es mit Menschen, die noch mit C# 4, wer so naiv zu glauben, dass es korrekt zu sein. Hoffentlich wird die Zahl der betroffenen Personen durch ein solches Szenario ist klein.
Als ein beiseite, ReSharper hat immer wieder dies und meldet es als "Zugang zu modifizierten Verschluss". Dann, durch drücken von Alt+Enter, wird es auch automatisch lösen Sie Ihren code für Sie. jetbrains.com/resharper
InformationsquelleAutor Eric Lippert
Was Sie Fragen ist gründlich abgedeckt, die von Eric Lippert in seinem blog-post Schließen über den loop-variable considered harmful und seine Nachfolger.
Ist für mich das überzeugendste argument ist, dass neue Variablen in jeder iteration unvereinbar wäre mit
for(;;)
- Stil-Schleife. Würden Sie erwarten, dass eine neueint i
in jeder iterationfor (int i = 0; i < 10; i++)
?Das häufigste problem mit diesem Verhalten macht eine Schließung Laufe der iteration variabel und es hat eine einfache Problemumgehung:
Mein blog-post zu diesem Thema: Schließung über foreach variable in C#.
Ja, es ist nicht möglich zu schließen, indem der Wert, jedoch gibt es eine sehr einfache Abhilfe, die ich eben bearbeitet haben meine Antwort zu gehören.
Es ist schade, das die Verschlüsse in C# - close über Referenzen. Wenn Sie geschlossen über Werte standardmäßig, könnten wir leicht angeben, schließen über Variablen statt mit
ref
.dies ist ein Fall, wo die erzwungene Konsistenz ist viel schädlicher als inkonsistent. Es soll "einfach funktionieren", wie die Leute erwarten, und klar die Leute erwarten, dass etwas anderes bei der Verwendung von foreach im Gegensatz zu einer for-Schleife, da die Zahl der Menschen, die auf Probleme stoßen, bevor wir wussten, dass über den Zugang zu modifizierten Verschluss-Problem (wie mich).
weiß nicht C#, aber in Common LISP gibt es eine syntax dafür zahlen, dass jede Sprache mit einer veränderlichen Variablen und Verschlüsse würde (Nein, muss) haben es auch. Wir schließen Sie entweder über die Referenz an die sich verändernden Ort, oder ein Wert zu einem bestimmten Zeitpunkt (Erstellung einer Schließung). Dieser beschreibt ähnliches in Python und Scheme (
cut
für refs/vars undcute
für das halten von ausgewertet - Werte in teilweise ausgewertet Verschlüsse).InformationsquelleAutor Krizz
Haben gebissen hat, habe ich die Angewohnheit, einschließlich der lokal definierten Variablen in dem innersten Umfang, die ich verwenden, um übertragung auf eine Schließung. In deinem Beispiel:
Ich tun:
Sobald Sie das haben, Gewohnheit haben, können Sie es vermeiden in die sehr seltenen Fall, dass Sie tatsächlich beabsichtigt zu binden, um die äußeren Bereiche. Um ehrlich zu sein, ich glaube nicht, dass ich jemals damit fertig werden.
InformationsquelleAutor Godeke
In C# 5.0, dieses problem ist behoben und Sie können dicht über loop-Variablen und die Ergebnisse, die Sie erwarten.
Die Sprache, die Spezifikation sagt:
Achten Sie darauf, Lesen Sie die Eric ' s Antwort: C# 1.0-Spezifikation (in deinem link reden wir über VS 2003, D. H. C# 1.2) nicht sagen,, ob die loop-variable war der innerhalb oder außerhalb der Schleife, da es keinen beobachtbaren Unterschied. Beim closure-Semantik eingeführt wurden, die in C# 2.0, die Wahl wurde gemacht, um die loop-variable außerhalb der Schleife, im Einklang mit der "for" - Schleife.
Also sagen Sie, dass die Beispiele in dem link waren nicht die endgültige Skillung zu dieser Zeit?
Sie waren endgültigen Spezifikationen. Das problem ist, dass wir reden über eine Funktion (d.h. Funktion Verschlüsse), die später eingeführt wurde (mit C# 2.0). Wenn C# 2.0 kam, beschlossen Sie, setzen Sie die loop-variable außerhalb der Schleife. Und als änderten Sie Ihre Meinung wieder mit C# 5.0 🙂
InformationsquelleAutor Paolo Moretti
Meiner Meinung nach, seltsame Frage. Es ist gut zu wissen, wie der compiler funktioniert aber nur "gut zu wissen".
Wenn Sie code schreiben, hängt davon ab, Algorithmus, compiler, es ist eine schlechte Praxis.Und es ist besser, neu zu schreiben code, um auszuschließen, diese Abhängigkeit.
Dies ist eine gute Frage für das Vorstellungsgespräch. Aber im echten Leben, ich ' V nicht konfrontiert mit allen Problemen, die ich gelöst Vorstellungsgespräch.
90% der foreach verwendet für Prozess jedes element von collection (nicht für select oder berechnen Sie einige Werte). manchmal müssen Sie berechnen einige Werte, die innerhalb der Schleife, aber es ist keine gute Praxis, um einen GROßEN loop.
Es ist besser zu Verwendung von LINQ-Ausdrücke zur Berechnung der Werte. Da bei der Berechnung eine Menge, was innerhalb der Schleife, nach 2-3 Monat, wenn Sie (oder jemand anderes) Lesen Sie diese code-person werden nicht verstehen, was ist das und wie sollte es funktioniert.
"Es ist besser zu Verwendung von LINQ-Ausdrücke zur Berechnung der Werte." Sie werden bemerken, dass ich mit der Schleife Aufbau einer LINQ-Ausdruck. ja, mit
Aggregate
vermieden hätte, dieses problem in den ersten Platz, aber es ist sehr Häufig für die Menschen zu verwenden, - Schleifen wie diese. Ich sehe Fragen, die sich aus dieser Art von problem in JavaScript die ganze Zeit auf StackOverflow, und Sie waren ähnlich Häufig in C# vor dem team beschlossen, dies zu ändern, Sprache, Verhalten.InformationsquelleAutor TemaTre