C# 内置于 foreach 循环的 Linq 查询总是从上次迭代中获取参数值
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/295593/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Linq query built in foreach loop always takes parameter value from last iteration
提问by Boris Callens
I have a List containing several keywords. I foreach through them building my linq query with them like so (boiled down to remove the code noise):
我有一个包含多个关键字的列表。我通过他们 foreach 像这样构建我的 linq 查询(归结为消除代码噪音):
List<string> keys = FillKeys()
foreach (string key in keys){
q = q.Where(c => c.Company.Name.Contains(key));
}
When I now make my keys contain 2 keys that return results seperatly, but can never occure together (every item in q is either "xyz" or "123", never "123" AND "xyz"), I still get results. The resultset is then the same as the last string it got to.
当我现在让我的键包含 2 个单独返回结果的键,但永远不会一起出现(q 中的每个项目都是“xyz”或“123”,永远不会是“123”和“xyz”),我仍然得到结果。结果集与它得到的最后一个字符串相同。
I had a look at the linq query and it appears it creates the correct sql, but it replaces @p1 AND @p2 both by the same (last itterated) value.
我查看了 linq 查询,它似乎创建了正确的 sql,但它将 @p1 和 @p2 都替换为相同的(最后一次迭代的)值。
What am I doing wrong?
我究竟做错了什么?
采纳答案by Jon Skeet
You're reusing the same variable (key
) in your lambda expression.
您key
在 lambda 表达式中重用了相同的变量 ( )。
See my article on anonymous methodsfor more details, and there are a number of related SO questions too:
有关更多详细信息,请参阅我关于匿名方法的文章,还有许多相关的 SO 问题:
- LINQ to SQL bug (or very strange feature)...
- Local variables with delegates
- C# captured variable in a loop
- C# gotcha answer
- Building a LINQ query programmatically without local variables tricking me
The simple fix is to copy the variable first:
简单的解决方法是先复制变量:
List<string> keys = FillKeys()
foreach (string key in keys){
string copy = key;
q = q.Where(c => c.Company.Name.Contains(copy));
}
回答by Marc Gravell
Possibly a captured variable issue; try adding:
可能是捕获的变量问题;尝试添加:
List<string> keys = FillKeys()
foreach (string key in keys){
string tmp = key;
q = q.Where(c => c.Company.Name.Contains(tmp));
}
回答by lukaszk
it's been fixed in C# 5.0, and the example above in C# 5.0 works but fails in earlier versions of C#.
它已在 C# 5.0 中得到修复,上面的示例在 C# 5.0 中有效,但在早期版本的 C# 中失败。
But be careful, it does not concern a for loop
但要小心,它与 for 循环无关
static void Main()
{
IEnumerable<char> query = "aaa bbb ccc";
string lettersToRemove = "ab";
Console.WriteLine("\nOK with foreach:");
foreach (var item in lettersToRemove)
{
query = query.Where(c => c != item);
}
foreach (char c in query) Console.Write(c);
//OK:
Console.WriteLine("\nOK with foreach and local temp variable:");
query = "aaa bbb ccc";
foreach (var item in lettersToRemove)
{
var tmp = item;
query = query.Where(c => c != tmp);
}
foreach (char c in query) Console.Write(c);
/*
An IndexOutOfRangeException is thrown because:
firstly compiler iterates the for loop treating i as an outsite declared variable
when the query is finnaly invoked the same variable of i is captured (lettersToRemove[i] equals 3) which generates IndexOutOfRangeException
The following program writes aaa ccc instead of writing ccc:
Each iteration gets the same variable="C", i (last one frome abc).
*/
//Console.WriteLine("\nNOK with for loop and without temp variable:");
//query = "aaa bbb ccc";
//for (int i = 0; i < lettersToRemove.Length; i++)
//{
// query = query.Where(c => c != lettersToRemove[i]);
//}
//foreach (char c in query) Console.Write(c);
/*
OK
The solution is to assign the iteration variable to a local variable scoped inside the loop
This causes the closure to capture a different variable on each iteration.
*/
Console.WriteLine("\nOK with for loop and with temp variable:");
query = "aaa bbb ccc";
for (int i = 0; i < lettersToRemove.Length; i++)
{
var tmp = lettersToRemove[i];
query = query.Where(c => c != tmp);
}
foreach (char c in query) Console.Write(c);
}