2013-02-27 11 views
35

1. Aussage:Warum sind diese Linq-Ausgaben unterschiedlich?

IEnumerable<char> query = "Not what you might expect"; 

query = query.Where (c => c != 'a'); 
query = query.Where (c => c != 'e'); 
query = query.Where (c => c != 'i'); 
query = query.Where (c => c != 'o'); 
query = query.Where (c => c != 'u'); 

Ausgabe von String.Join("", query): "Nt wht y mght xpct"

2. Aussage:

query = "Not what you might expect"; 

foreach (char vowel in "aeiou") 
    query = query.Where (c => c != vowel); 

Ausgabe von String.Join("", query): "Not what yo might expect"

Die Ausgaben von diesen Aussagen abweichen. Kann jemand erklären warum?

+4

Was ist die Ausgabe, die Sie erhalten? – Default

+1

Die Ergebnisse hängen davon ab, auf welche Version von .NET Sie abzielen - welcher Version steht dies entgegen? – goric

+1

Ist das wirklich wie Ihr Code strukturiert ist? Der Wert "Vokal" muss im zweiten Beispiel aufgehoben werden, sonst wird er nur 5 Mal als '! = 'U'' ausgeführt. –

Antwort

57

Wenn Sie eine C# Version niedriger als 5,0 (wobei dies behoben wurde) verwendet wird, ist dies der Grund:

Das Lambda in der Abfrage erfasst die Schleifenvariable vowel.
Da Linq die verzögerte Ausführung gerne verwendet, wird der Wert dieser Referenz erst gelesen, wenn die Abfrage ausgeführt wird (durch Iteration), dh nachdem die foreach Schleife beendet wurde. An diesem Punkt ist der neueste Wert von vowelu, weshalb Sie die unerwartete Ausgabe erhalten.

Sie können dies umgehen, indem Sie den Wert in eine andere temporäre Variable kopieren (oder indem Sie auf C# 5.0 aktualisieren).

Versuchen Sie folgendes:

query = "Probably what you might expect"; 

foreach (char vowel in "aeiou") { 
    char currentVowel = vowel; 
    query = query.Where (c => c != currentVowel); 
} 
+0

Nur neugierig, wann hat C# das behoben? Wo kann ich über diesen Fix lesen? Vielen Dank. –

+3

Oh, [ich sehe] (http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-consided-harmful.aspx), in C# 5 –

+2

Ich frage mich, warum das in .net 4.5 "behoben" wurde. Da LINQ per Definition deferred-execution ist, sehe ich nicht, warum das alte Verhalten geändert werden soll, was eigentlich richtiger ist. – Teejay

11

Lesen Sie mehr über Verschluss. Wenn Sie .NET 4.0 und darunter verwenden, erhalten Sie ein anderes Ergebnis. In .NET 4.5 wird dieses Verhalten geändert (behoben). Siehe auch, wie der Compiler erweitert wird foreach.

+0

Für diesen Artikel http://msmvps.com/blogs/kathleen/archive/2012/07/03/lifting-foreach-breaking-change-in-visual-studio-2012.aspx scheint es eine Änderung in VS zu sein 2012 Compiler, anstatt in C# 5. In der Tat habe ich gerade mit VB auf Framework 3.5 versucht und es gibt das neue * erwartete * Ergebnis. Bitte modifiziere deine Antwort, um zukünftige Leser nicht zu verwirren. – Teejay

+1

@Teejay Dieses Verhalten wird in C# 5.0-Spezifikation beschrieben. Auch die Compiler sind Teil von .NET Framework. –

+1

Wahrscheinlich. Aber du hast * geschrieben "Wenn du .NET 4.0 verwendest und unten, bekommst du ein anderes Ergebnis." *. Das ist nicht wahr. Habe gerade mal 3.5 probiert und es verhält sich wie der neue 4.5. – Teejay

13

Es ist, weil Sie eine Schließung über die vowel Variable erstellen, die sich im Laufe der Zeit ändert. Speichern Sie ihren Wert in einer separaten Variablen und es wird funktionieren:

query = "Not what you might expect"; 

foreach (char vowel in "aeiou") 
{ 
    var current = vowel; 
    query = query.Where (c => c != current); 
} 
Verwandte Themen