【问题标题】:Why does Select turn my while loop into an infinite loop?为什么 Select 将我的 while 循环变成无限循环?
【发布时间】:2019-08-05 11:30:52
【问题描述】:

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

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

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

虽然我不必再处理不再终止的代码,但我仍然想知道这种行为最初是从哪里来的。

【问题讨论】:

  • .Any 枚举列表,导致构造函数触发。因此,在每个 .Any 上,都会创建 3 个新对象,其中 2 个为 False,无限。你可以通过打破构造函数来看到这一点。

标签: c# linq


【解决方案1】:

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

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

无论您何时调用它,它都会创建一个List&lt;bool&gt;() {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&lt;T&gt;,它被创建一次,您可以在其中修改第一个和第三个项目:

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

【讨论】:

  • 真的每次都创建一个初始List的新实例吗?我认为问题在于每次都在 Select 中创建新的 WrappedBool。
  • @Pseudoradius:Linq 使用延迟执行;它在调用之前什么都不做,每次调用它时都会执行查询。物化将 query 转化为 collection (.ToList(), .ToArray()), value (.Count()) 并执行一次;然后你像往常一样使用这个集合、值等。
  • 我想我现在明白了。谢谢你的帮助。 :)
猜你喜欢
  • 1970-01-01
  • 2017-09-21
  • 1970-01-01
  • 2018-03-17
  • 1970-01-01
  • 2016-07-27
  • 2015-12-24
  • 2018-11-01
  • 1970-01-01
相关资源
最近更新 更多