为什么foreach中需要的临时变量包含在lambda表达式中?

时间:2012-03-24 22:00:43

标签: linq c#-4.0 lambda iqueryable

我正在阅读C# 4 in a Nutshell,我已经看到了这段代码:

IQueryable<Product> SearchProducts (params string[] keywords)
{
  IQueryable<Product> query = dataContext.Products;

  foreach (string keyword in keywords)
  {
    string temp = keyword;
    query = query.Where (p => p.Description.Contains (temp));
  }
  return query;
}

在代码之后有一个“警告”,如下所示:

The temporary variable in the loop is required to avoid the outer variable trap, where the same variable is captured for each iteration of the foreach loop.

我没理解,我不明白为什么temp变量是必要的。什么是outter variable trap

来自:http://www.albahari.com/nutshell/predicatebuilder.aspx

有人可以澄清一下吗?

2 个答案:

答案 0 :(得分:5)

因为只有一个名为keyword一个变量被关闭。但是,临时变量在每次迭代时不同

因此,如果没有临时变量,当lambda 稍后执行时,keyword将计算它在循环中分配的最后一个值。

在某些情况下,另一种解决方案是强制评估(并最终得到List等等。

答案 1 :(得分:2)

问题在于keyword正在发生变化。 =>委托关闭变量的当前值,而不是创建委托时过去的值。在Eric Lippert's blog post中有详细解释。

这个经典的C bug也是同样的错误:

#include <stdio.h> 
#include <pthread.h>

void * MyThreadFunction(void *x)
{
   printf("I am thread %d\n", * (int *) x);
   return NULL;
}

int main(void)
{
   int i;
   pthread_t t[10];
   void *ret;
   for(i=0; i<10; i++)
       pthread_create(&t[i], NULL, MyThreadFunction, (void *) &i);
   for(i=0; i<10; i++)
       pthread_join(t[i], &ret);
}

* (int *) x获取{em>当前值i,而不是创建线程时的值。