采用以下代码,改编自this question:
//Borrowed from another question because its a simpler example of what happened to me.
IEnumerable<char> query = "Not what you might expect";
foreach(char vowel in "aeiou")
{
query = query.Where(c => c != vowel);
}
foreach (char Output in query)
{
System.Out.WriteLine(Output);
}
这只会从查询字符集中删除“u”。核心问题与c
子句中的Where
变量在第二次foreach之前未被评估的事实有关。我的问题是:
1)为什么第一个foreach生成的委托在构建时不会捕获c
的每个值?是否有一些情况我不知道哪里不是理想的行为?
2)如果它没有捕获c
的值,那么当查询实际运行时,该值是否仍然在第二个foreach的范围内?在我看来,如果它没有存储传入的变量的值,那么尝试解析第二个foreach的语句将会失败,因为变量c
显然超出了范围。
我不明白'当它在范围内时,使用我们在这个变量上看到的最后一个值'对于这种情况来说是一个很好的设计决策,并且希望有人可以对这个问题有所了解。
答案 0 :(得分:7)
正在捕捉vowel
;尝试:
foreach(char vowel in "aeiou") {
char tmp = vowel;
query = query.Where(c => c != tmp);
}
Jon有相关帖子列表here。
至于为什么...... foreach
被定义为(在ECMA334v4§15.8.4中):
A foreach statement of the form `foreach (V v in x) embedded-statement` is then expanded to:
{
E e = ((C)(x)).GetEnumerator();
try {
V v;
while (e.MoveNext()) {
v = (V)(T)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
}
请注意,V
在之外 while
- 这意味着foreach
只会一次。我认为C#团队有时会怀疑这种变化的智慧(它在C#1.2规范中是内部 while
,这本来可以完全避免这个问题)。也许它最终会改变,但现在它符合规范。
答案 1 :(得分:4)
正如Marc所说,它将vowel
作为变量捕获,而不是每种情况下vowel
的值。这几乎总是不受欢迎的,但它是指定的行为(即编译器遵循语言规范)。每个迭代都有一个元音变量,而不是“新的变量实例”。
请注意,此处不涉及表达式树 - 您正在使用IEnumerable<T>
,因此它只是将lambda表达式转换为委托。
有关详细信息,请参阅C#3规范的第7.14.4节。
答案 2 :(得分:1)
我认为你真正想做的事情更像是这样:
IEnumerable<char> query = "Not what you might expect";
query = query.Except("aeiou");
foreach (char Output in query)
{
System.Out.WriteLine(Output);
}