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

Tim*_*ter 5 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" });
Run Code Online (Sandbox Code Playgroud)

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

someValues = someValues
    .OrderBy(t => t[0])
    .ThenBy(t => t[1])
    .ToList();
Run Code Online (Sandbox Code Playgroud)

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

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 
Run Code Online (Sandbox Code Playgroud)

但这不起作用,它IndexOutOfRangeException在最后一行失败了.调试器告诉我,i就是2在那个时候,这样的for循环条件似乎被忽略,i已经是== minDim.

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

Dir*_*irk 7

这与许多人在foreachC#5 之前的循环一样是个问题.

orderedValues = orderedValues.ThenBy(t => t[i]);
Run Code Online (Sandbox Code Playgroud)

i直到你调用.ToList()它为2时才会计算值,因为那是for循环的退出条件.

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

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

有关更多信息,您可以查看Eric Lippert关于关闭环路变量被认为有害的博客文章.


D S*_*ley 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(); 
Run Code Online (Sandbox Code Playgroud)