我观察到一种非常奇怪的行为,也许你可以帮助我看看会发生什么。
这里的课程:
public sealed class Sudoku
{
private SudokuCell[] _grid = new SudokuCell[81];
// ctor {}
private IEnumerable<SudokuCell> Grid
{
get { return _grid; }
}
private SudokuRow[] _rows;
public IEnumerable<SudokuRow> Rows
{
get
{
if (_rows == null)
{
_rows = new SudokuRow[9];
for (int i = 0, length = 9; i < length; i++)
{
_rows[i] = new SudokuRow(from cell in Grid
where cell.Row == i
select cell);
// Always print 9 (GOOD)
Trace.WriteLine("First Loop " + i + " : " + _rows[i].Cells.Count());
}
}
for (int i = 0; i < 9; i++)
{
// Always print 0 ! Huh !?
Trace.WriteLine("Second Loop " + i + " : " + _rows[i].Cells.Count());
}
return _rows;
}
}
}
public abstract class SudokuPart
{
public SudokuPart(IEnumerable<SudokuCell> cells)
{
Cells = cells;
}
public int Index
{ get; protected set; }
public IEnumerable<SudokuCell> Cells
{ get; protected set; }
}
public sealed class SudokuRow : SudokuPart
{
public SudokuRow(IEnumerable<SudokuCell> cells)
: base(cells)
{
base.Index = cells.First().Row;
}
}
有人能告诉我为什么在第二个循环中它追踪0而不是9!我在两个循环之间都没有改变!!!
...谢谢
答案 0 :(得分:10)
这是问题所在:
_rows[i] = new SudokuRow(from cell in Grid
where cell.Row == i
select cell);
在循环中捕获循环变量(i
)... ,它有一个合理的值,这就是你看到9个匹配的原因。
但是,当您计算第二个循环中的匹配值时,该单个捕获的变量将具有值9.现在否 cell.Row
的值为9,所以你是没有得到任何比赛。有关这方面的更多信息,请参阅Eric Lippert的精彩博客文章"Closing over the loop variable considered harmful."
三个修复:
捕获循环变量的副本:
int copy = i;
_rows[i] = new SudokuRow(from cell in Grid
where cell.Row == copy
select cell);
循环的每次迭代都会得到一个单独的副本。
在循环中实现查询:
_rows[i] = new SudokuRow((from cell in Grid
where cell.Row == i
select cell).ToList());
甚至:
_rows[i] = new SudokuRow(Grid.Where(cell => cell.Row == i).ToList());
根本不要使用LINQ!为什么不只是有一个数组数组来表示网格?这是一种更加自然的方法,IMO。
答案 1 :(得分:0)
我认为Jon Skeet的答案很棒,但我只是想通过Deferred LINQ Queries的例子添加一些内容。一旦我看到这一点,它帮助我更多地了解了你遇到的这种代码问题的一些细微差别。
试试这段代码。
var numbers = new List<int> {1, 2, 3, 4, 5};
//Lets create an IEnumerable<int> with the values in our numbers list greater then 3.
var bignumbers = numbers.Where(n => n > 3);
//You may assume our variable bignumbers now contains the numbers 4 and 5
//now lets add another number to our original list that fits the criteria of our LINQ Where statement
numbers.Add(6);
foreach (var big in bignumbers) {
Console.WriteLine(big.ToString());
}
我们foreach循环的输出将是4,5,6!这是因为我们的查询不会运行,直到foreach导致我们的bignumbers变量中的项的枚举。
当您的建筑物在循环中列出并且您在循环之外查询这些列表时,还需要考虑其他事项。你经常会得到一些不同于你期望的东西。