35

1st statement:

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

Output of String.Join("", query): "Nt wht y mght xpct"

2nd statement:

query = "Not what you might expect";

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

Output of String.Join("", query): "Not what yo might expect"

The outputs from these statements are different. Can any one explain why?

4

3 回答 3

57

If you're using a C# version lower than 5.0 (where this was fixed), this is the reason:

The lambda in your query captures the loop variable vowel.
Because Linq likes to use deferred execution, the value of this reference is not read until the query gets executed (by iterating over it), which is after the foreach loop has finished. At that point, the most recent value of vowel is u, which is why you get the unexpected output.

You can get around this by copying the value to another temporary variable (or by upgrading to C# 5.0).

Try this:

query = "Probably what you might expect";

foreach (char vowel in "aeiou") {
    char currentVowel = vowel;
    query = query.Where (c => c != currentVowel );
}
于 2013-02-27T13:41:34.540 回答
14

这是因为您在变量上创建了一个闭包,该vowel变量会随时间变化。将其值存储在一个单独的变量中,它将起作用:

query = "Not what you might expect";

foreach (char vowel in "aeiou")
{
    var current = vowel;
    query = query.Where (c => c != current);
}
于 2013-02-27T13:42:57.070 回答
11

Read about closure. If you use .NET 4.0 and below you will get different result. In .NET 4.5 this behavior is changed(fixed). See also how compiler expand foreach.

于 2013-02-27T13:41:47.210 回答