For-Loop和LINQ的延迟执行不能很好地协同工作

时间:2014-06-10 15:21:51

标签: c# linq for-loop .net-4.0

标题表明我已经知道发生了什么,但我无法解释。我试图通过每个“列”动态订购List<string[]>,从第一列开始,以所有数组的最小Length结束。

因此,在此示例中,它是2,因为最后一个string[]只有两个元素:

List<string[]> someValues = new List<string[]>();
someValues.Add(new[] { "c", "3", "b" });
someValues.Add(new[] { "a", "1", "d" });
someValues.Add(new[] { "d", "4", "a" });
someValues.Add(new[] { "b", "2" });

现在我试图通过第一和第二列排序。我可以用这种方式静态地做到这一点:

someValues = someValues
    .OrderBy(t => t[0])
    .ThenBy(t => t[1])
    .ToList();

但如果我不知道“列”的数量,我可以使用这个循环(这就是我的想法):

int minDim = someValues.Min(t => t.GetLength(0));  // 2
IOrderedEnumerable<string[]> orderedValues = someValues.OrderBy(t => t[0]);
for (int i = 1; i < minDim; i++)
{
    orderedValues = orderedValues.ThenBy(t => t[i]);
}
someValues = orderedValues.ToList();  // IndexOutOfRangeException 

但是这不起作用,它在最后一行失败了IndexOutOfRangeException。调试器告诉我那时i2,因此似乎忽略了for循环条件,i已经== minDim

为什么会这样?这是正确的方法是什么?

2 个答案:

答案 0 :(得分:7)

这与许多人在C#5之前使用foreach循环的问题相同。

orderedValues = orderedValues.ThenBy(t => t[i]);

i的值只有在你调用.ToList()时才会被评估,因为那是for循环的退出条件。

您可以在for循环中引入一个新的局部变量来修复它:

for (int i = 1; i < minDim; i++)
{
    var tmp = i;
    orderedValues = orderedValues.ThenBy(t => t[tmp]);
}

有关更多信息,您可以查看Eric Lippert关于Closing over the loop variable considered harmful的博客文章。

答案 1 :(得分:6)

可能会发生这种情况,因为i的值未在循环内关闭 - 当循环退出时,i的值为2 t[i]

一种解决方案是在循环中创建一个结束变量:

int minDim = someValues.Min(t => t.GetLength(0));  // 2
IOrderedEnumerable<string[]> orderedValues = someValues.OrderBy(t => t[0]);
for (int i = 1; i < minDim; i++)
{
    var x = i;
    orderedValues = orderedValues.ThenBy(t => t[x]);
}
someValues = orderedValues.ToList();