简体   繁体   English

奇怪的数组行为

[英]Strange array behavior

I observe a very strange behavior, maybe could you help me to see what happen. 我观察到一种非常奇怪的行为,也许你可以帮助我看看会发生什么。

Here the class: 这里的课程:

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;
    }
}

Could anyone tell me why in the second loop it trace 0 instead of 9 !? 谁能告诉我为什么在第二个循环中它跟踪0而不是9! I changed nothing between both loops !!! 我在两个循环之间都没有改变!

Thanks... 谢谢...

This is the problem: 这就是问题:

_rows[i] = new SudokuRow(from cell in Grid
                         where cell.Row == i
                         select cell);

That's capturing the loop variable ( i )... within the loop, it has a sensible value, which is why you're seeing 9 matches. 这是捕获循环变量( i )... 循环内,它有一个合理的值,这就是你看到9个匹配的原因。

However, when you count the matching values in the second loop, that single captured variable will have the value 9. Now no cell.Row has a value of 9, so you're not getting any matches. 但是,当您计算第二个循环中的匹配值时,该单个捕获的变量将具有值9.现在没有 cell.Row的值为9,因此您没有获得任何匹配。 For more information on this, see Eric Lippert's great blog post, "Closing over the loop variable considered harmful." 有关这方面的更多信息,请参阅Eric Lippert的精彩博文, “关闭循环变量被视为有害”。

Three fixes: 三个修复:

  • Capture a copy of the loop variable: 捕获循环变量的副本

     int copy = i; _rows[i] = new SudokuRow(from cell in Grid where cell.Row == copy select cell); 

    Each iteration of the loop will get a separate copy. 循环的每次迭代都将获得一个单独的副本。

  • Materialize the query in the loop: 在循环中实现查询:

     _rows[i] = new SudokuRow((from cell in Grid where cell.Row == i select cell).ToList()); 

    Or even: 甚至:

     _rows[i] = new SudokuRow(Grid.Where(cell => cell.Row == i).ToList()); 
  • Don't use LINQ at all! 根本不要使用LINQ! Why not just have an array of arrays to represent the grid? 为什么不只是有一个数组数组来表示网格? That's a much more natural approach, IMO. 这是一种更自然的方法,IMO。

I think Jon Skeet answer is great, but I just wanted to add a bit to it with an example of Deferred LINQ Queries. 我认为Jon Skeet的答案很棒,但我只是想通过Deferred LINQ Queries的例子添加一些内容。 Once I saw this in action, it helped me understand a bit more about some of the nuances of this kind of code problem you ran into. 一旦我看到这一点,它帮助我更多地了解了你遇到的这种代码问题的一些细微差别。

Try this code. 试试这个代码。

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());
}

Our output from our foreach loop is going to be 4,5,6! 我们foreach循环的输出将是4,5,6! This is because our query doesn't run until the foreach causes the enumeration of the items in our bignumbers variable. 这是因为我们的查询不会运行,直到foreach导致我们的bignumbers变量中的项的枚举。

Just something else to consider when your building lists within loops and your querying those lists outside of the loops. 当您的建筑物在循环中列出并且您在循环之外查询这些列表时,还需要考虑其他事项。 Your often going to get something other than what your expecting. 你经常会得到一些不同于你期望的东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM