如何在循环中使用linq?

时间:2011-11-01 20:37:42

标签: c# linq

让我们从一些要查询的源数据开始:

int[] someData = { 1, 2 };

运行以下代码后,事情就像我期望的那样:a包含2个元素,这些元素可归结为12someData拉出。

List<IEnumerable<int>> a = new List<IEnumerable<int>>();
a.Add(someData.Where(n => n == 1));
a.Add(someData.Where(n => n == 2));

这个下一个代码只在循环中执行完全相同的操作,但不能按预期工作。当此代码完成时,b包含2个元素但它们都相同 - 指向2。在第二个循环中,它修改b的第一个元素。

List<IEnumerable<int>> b = new List<IEnumerable<int>>();
for (int i = 1; i <= 2; ++i)
{
    b.Add(someData.Where(n => n == i));
}

为什么会发生这种情况?如何使循环版本的行为与第一个版本相同?

4 个答案:

答案 0 :(得分:7)

Jon Skeet有一个很好的答案here

您需要将i分配给temp变量,并在Linq查询中使用该变量

List<IEnumerable<int>> b = new List<IEnumerable<int>>();
for (int i = 1; i <= 2; ++i)
{
    int temp = i;
    b.Add(someData.Where(n => n == temp));
}

答案 1 :(得分:4)

你的问题是懒惰的评价。您可以将Enumerable添加到代表b的{​​{1}}。每当您查看someData.Where(n => n == i)的元素b时,就会对此进行评估。

您希望通过在其上调用iToArray()来显示可枚举项。

ToList()

或者,您可以缩小捕获变量的范围:

for (int i = 1; i <= 2; ++i)
{
    b.Add(someData.Where(n => n == i).ToArray());
}

然后你仍然有懒惰的评估枚举(当你修改for (int i = 1; i <= 2; ++i) { int localI=i; b.Add(someData.Where(n => n == localI)); } 时显示),但每个都有不同的someData

答案 2 :(得分:3)

对于循环中where子句的每个声明,都有一个单独的Func&lt; int,bool&gt;正在传递给它的实例。由于i的值被传递给与Func&lt; int,bool&gt;的每个实例相关联的lambda函数。 (Func&lt; int,bool&gt;本质上是委托),i是在Func&lt; int,bool&gt;的每个实例之间共享的捕获变量。

换句话说,即使在循环范围之外,我必须“保持活着”,以便在任何Func&lt; int,bool&gt;的实例时评估其值。被调用。通常,这不会是一个问题,除非在必要之前不会发生调用(即需要枚举查询的结果,委托实例被传递给。例如,foreach循环:仅然后调用委托以便为迭代目的确定查询结果。)

这意味着循环中声明的LINQ查询在for循环结束后的某个时间才会真正生成,这意味着我将设置为2的值。结果,你实际上在做像这样的东西:

 b.Add(someData.Where(n => n == 2)); 
 b.Add(someData.Where(n => n == 2));

为防止这种情况发生,对于循环中的每次迭代,您需要声明整数类型的单独实例并使其等效于i。将此传递给迭代中声明的lambda函数以及Func&lt; int,bool&gt;的每个实例。将有一个单独的捕获变量,其值在每次后续迭代后都不会被修改。例如:

 for (int i = 1; i <= 2; ++i)  
 {
      int j = i;  
      b.Add(someData.Where(n => n == j));  
 } 

答案 3 :(得分:1)

这是捕获的loop-var问题的略微隐藏的变化。

你可以这样解决:

List<IEnumerable<int>> b = new List<IEnumerable<int>>();
for (int i = 1; i <= 2; ++i)
{
    int j = i;  // essential
    b.Add(someData.Where(n => n == j));
}

但更直观的是

List<IEnumerable<int>> b = new List<IEnumerable<int>>();
for (int i = 1; i <= 2; ++i)
{        
    b.Add(someData.Where(n => n == i).ToList());
}

原始代码中发生的事情是捕获变量i(关闭)并将引用存储在lambdas中。结果是IEnumerable s,menas延期执行。 i的值仅在显示/检查结果时获取,到那时它是2