2013-02-27 14 views
35

第一の文:String.Join("", query)これらのlinq出力はなぜ異なっていますか?

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'); 

出力:"Nt wht y mght xpct"

第二の文:

query = "Not what you might expect"; 

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

String.Join("", query)の出力:"Not what yo might expect"

これらのステートメントからの出力は異なります。 理由を説明できる人はいますか?クエリで

ラムダは、ループ変数vowelをキャプチャ:

+4

どのような出力が得られますか? – Default

+1

この結果は、対象とする.NETのバージョンに依存します。これはどちらのバージョンですか? – goric

+1

これは実際にコードがどのように構造化されていますか? 2番目の例では 'vowel'の値を上げる必要があります。それ以外の場合は'!= 'u''として5回だけ実行されます。 –

答えて

57

あなたは(これが修正されました)C#バージョン5.0未満を使用している場合は、これが理由です。
Linqは遅延実行の使用が好きなので、ループが終了した後である(繰り返し実行する)まで、この参照の値は読み込まれません。その時点で、vowelの最新の値はuであり、予期しない出力が得られます。

これを回避するには、別の一時変数に(またはC#5.0にアップグレードして)値をコピーします。閉鎖について

query = "Probably what you might expect"; 

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

ちょうど興味がありますが、いつC#がこれを修正しましたか?この修正プログラムはどこで読むことができますか?ありがとう。 –

+3

ああ、C#5の[私が知る](http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx) –

+2

なぜこれが.net 4.5で "修正"されているのだろうか。 LINQは定義によって遅延実行されるため、実際にはより正確な古い動作を変更する理由はわかりません。 – Teejay

11

読む:

はこれを試してみてください。 .NET 4.0以下を使用すると、異なる結果になります。 .NET 4.5では、この動作が変更(固定)されています。コンパイラの展開方法も参照してください。foreach

+0

この記事についてhttp://msmvps.com/blogs/kathleen/archive/2012/07/03/lifting-foreach-breaking-change-in-visual-studio-2012.aspx VSの変更のようですC#5ではなく2012年のコンパイラです。実際には、私はフレームワーク3.5でVBを試してみました。将来の読者を混乱させないようにあなたの答えを修正してください。 – Teejay

+1

@Teejayこの動作はC#5.0仕様で説明されています。また、コンパイラは.NETフレームワークの一部です。おそらく –

+1

。しかし、あなたが書きました。「.NET 4.0以降を使用すると、結果は変わります。それは真実ではない。ちょうど3.5で試してみて、それは新しい4.5として動作します。 – Teejay

13

vowel変数の上にクロージャを作成するのは、時間が変わるからです。その値を別の変数に格納すれば正常に動作します:

query = "Not what you might expect"; 

foreach (char vowel in "aeiou") 
{ 
    var current = vowel; 
    query = query.Where (c => c != current); 
} 
関連する問題