explanation - string for each c#




Gibt es einen Grund für C#Wiederverwendung der Variablen in einem foreach? (3)

Der Compiler deklariert die Variable in einer Weise, die sie sehr anfällig für einen Fehler macht, der oft schwer zu finden und zu debuggen ist, während sie keine wahrnehmbaren Vorteile erzeugt.

Ihre Kritik ist völlig berechtigt.

Ich diskutiere dieses Problem im Detail hier:

Schließen der Schleife Variable als schädlich

Gibt es etwas, was Sie mit foreach-Schleifen so machen können, dass Sie nicht könnten, wenn sie mit einer internen Variable kompiliert würden? Oder ist dies nur eine willkürliche Wahl, die getroffen wurde, bevor anonyme Methoden und Lambda-Ausdrücke verfügbar oder üblich waren und die seitdem nicht überarbeitet wurden?

Letzteres. Die C # 1.0-Spezifikation sagte tatsächlich nicht, ob die Schleifenvariable innerhalb oder außerhalb des Schleifenkörpers war, da sie keinen beobachtbaren Unterschied machte. Wenn in C # 2.0 die Closing-Semantik eingeführt wurde, wurde die Wahl getroffen, die Schleifenvariable außerhalb der Schleife zu setzen, und zwar in Übereinstimmung mit der "for" -Schleife.

Ich denke, es ist fair zu sagen, dass alle diese Entscheidung bedauern. Dies ist einer der schlimmsten "Fallstricke" in C #, und wir werden die bahnbrechende Änderung vornehmen , um es zu beheben. In C # 5 befindet sich die foreach-Loop-Variable logisch innerhalb des Schleifenkörpers und daher erhalten die Schließungen jedes Mal eine neue Kopie.

Die for Schleife wird nicht geändert, und die Änderung wird nicht auf vorherige Versionen von C # zurück portiert. Sie sollten daher weiterhin vorsichtig sein, wenn Sie dieses Idiom verwenden.

Bei der Verwendung von Lambda-Ausdrücken oder anonymen Methoden in C # müssen wir uns vor dem Zugriff auf modifizierte Fallgruben schützen. Beispielsweise:

foreach (var s in strings)
{
   query = query.Where(i => i.Prop == s); // access to modified closure
   ...
}

Aufgrund des modifizierten Abschlusses wird der obige Code dazu führen, dass alle Where Klauseln der Abfrage auf dem endgültigen Wert von s basieren.

Wie here erklärt, geschieht dies, weil die s Variable, die oben in der foreach Schleife deklariert wurde, im Compiler wie folgt übersetzt wird:

string s;
while (enumerator.MoveNext())
{
   s = enumerator.Current;
   ...
}

anstatt wie folgt:

while (enumerator.MoveNext())
{
   string s;
   s = enumerator.Current;
   ...
}

Wie here , gibt es keinen Leistungsvorteil, eine Variable außerhalb der Schleife zu deklarieren, und unter normalen Umständen kann ich mir den einzigen Grund dafür vorstellen, wenn Sie die Variable außerhalb des Bereichs der Schleife verwenden möchten:

string s;
while (enumerator.MoveNext())
{
   s = enumerator.Current;
   ...
}
var finalString = s;

Variablen, die in einer foreach Schleife definiert sind, können jedoch nicht außerhalb der Schleife verwendet werden:

foreach(string s in strings)
{
}
var finalString = s; // won't work: you're outside the scope.

Daher deklariert der Compiler die Variable auf eine Weise, die sie sehr anfällig für einen Fehler macht, der oft schwer zu finden und zu debuggen ist, während sie keine wahrnehmbaren Vorteile erzeugt.

Gibt es etwas, was Sie mit foreach Schleifen auf diese Weise tun können, wenn Sie mit einer inner-scoped-Variablen kompiliert wurden, oder ist dies nur eine willkürliche Entscheidung, die getroffen wurde, bevor anonyme Methoden und Lambda-Ausdrücke verfügbar oder üblich waren und welche wurde seitdem nicht überarbeitet?


Da ich davon gebissen bin, habe ich die Angewohnheit, lokal definierte Variablen in den innersten Gültigkeitsbereich aufzunehmen, die ich für die Übertragung an einen beliebigen Abschluss verwende. In Ihrem Beispiel:

foreach (var s in strings)
{
    query = query.Where(i => i.Prop == s); // access to modified closure

Ich mache:

foreach (var s in strings)
{
    string search = s;
    query = query.Where(i => i.Prop == search); // New definition ensures unique per iteration.

Sobald Sie diese Gewohnheit haben, können Sie sie in dem sehr seltenen Fall vermeiden, in dem Sie tatsächlich an die äußeren Bereiche binden wollten. Um ehrlich zu sein, glaube ich nicht, dass ich es jemals getan habe.


Was du fragst, wird von Eric Lippert in seinem Blogpost über die Loop-Variable, die als schädlich betrachtet wird, und dessen Fortsetzung umfassend behandelt.

Für mich ist das überzeugendste Argument, dass eine neue Variable in jeder Iteration nicht mit der for(;;) -Stil-Schleife übereinstimmt. Würden Sie erwarten, in jeder Iteration von for (int i = 0; i < 10; i++) ein neues int i zu haben?

Das häufigste Problem bei diesem Verhalten ist das Schließen einer Iterationsvariablen, und es gibt eine einfache Umgehungsmöglichkeit:

foreach (var s in strings)
{
    var s_for_closure = s;
    query = query.Where(i => i.Prop == s_for_closure); // access to modified closure

Mein Blogbeitrag zu diesem Thema: Closure over foreach Variable in C # .





anonymous-methods