在工作中,我遇到了一个奇怪的问题,我预期要终止的循环实际上无限期地运行。
我将问题追溯到使用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;
}
虽然我不必处理代码不再终止的问题,但我仍然想知道这种行为最初来自何处。
答案 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
是具体化的,因此enumerable
是List<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;
}