为什么Select会将我的while循环变成无限循环?

时间:2019-08-05 11:30:52

标签: c# linq

在工作中,我遇到了一个奇怪的问题,我预期要终止的循环实际上无限期地运行

我将问题追溯到使用Select。 有趣的是,当我在.ToList()之后添加Select时,循环终止了。我把它简化为一个小例子。

class WrappedBool
{
    public WrappedBool(bool inner)
    {
        InnerBool = inner;
    }
    public bool InnerBool { get; set; } = false;
}

// remove .ToList() here and the following loop will go infinite
IEnumerable<WrappedBool> enumerable = 
   new List<bool>() { false, true, false }
  .Select(b => new WrappedBool(b))
  .ToList();

while (enumerable.Any(wb => !wb.InnerBool))
{
    WrappedBool firstFalse = enumerable.Where(wb => !wb.InnerBool).First();
    firstFalse.InnerBool = true;
}

虽然我不必处理代码不再终止的问题,但我仍然想知道这种行为最初来自何处。

1 个答案:

答案 0 :(得分:6)

好吧,没有物化.ToList()enumerable只是一个查询

IEnumerable<WrappedBool> enumerable = 
   new List<bool>() { false, true, false }
  .Select(b => new WrappedBool(b));

无论何时调用它,它都会创建一个List<bool>() {false, true, false}新实例,其中有false个项目要进行迭代

// new List<bool>() { false, true, false } - do we have any false item here?
// Yes - keep on looping (forever)
while (enumerable.Any(wb => !wb.InnerBool)) 
{
    // get 1st false from new List<bool>() { false, true, false }
    WrappedBool firstFalse = enumerable.Where(wb => !wb.InnerBool).First();
    // turn it into true and discard
    firstFalse.InnerBool = true;
}

相反

IEnumerable<WrappedBool> enumerable = 
   new List<bool>() { false, true, false }
  .Select(b => new WrappedBool(b))
  .ToList(); // create a List; call new List<bool>() { false, true, false } just once

具体化的,因此enumerableList<T>,它是一次创建的,您可以在其中修改1st和3d项:

// does enumerable collection (List) have any false item? 
while (enumerable.Any(wb => !wb.InnerBool)) 
{
    // get 1st false from enumerable List
    WrappedBool firstFalse = enumerable.Where(wb => !wb.InnerBool).First();
    // turn it into true 
    firstFalse.InnerBool = true;
}