At work I ran into a strange problem, where a loop I expected to terminate was actually running indefinitely.
I traced the problem back to a use of Select
.
Interestingly, the loop terminated as expected when I added a .ToList()
right after the Select
. I boiled it down to a small example.
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;
}
While I don't have to deal with my code not terminating anymore, I still wonder where this behaviour is coming from in the first place.
Basically, the infinite loop happens when the condition in the while loop always evaluates to true. This can happen when the variables within the loop aren't updated correctly, or aren't updated at all. Let's say you have a variable that's set to 10 and you want to loop while the value is less than 100.
An infinite loop is a loop that runs indefinitely and it only stops with external intervention or when a break statement is found. You can stop an infinite loop with CTRL + C . You can generate an infinite loop intentionally with while True . The break statement can be used to stop a while loop immediately.
A loop becomes infinite loop if a condition never becomes FALSE. You must use caution when using while loops because of the possibility that this condition never resolves to a FALSE value. This results in a loop that never ends. Such a loop is called an infinite loop.
Well, without materialization (.ToList()
) enumerable
is just a query
IEnumerable<WrappedBool> enumerable =
new List<bool>() { false, true, false }
.Select(b => new WrappedBool(b));
whenever you call it, it creates a new instance of List<bool>() {false, true, false}
where you have false
items to iterate on
// 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;
}
On the contrary
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
is materialized so enumerable
is List<T>
which is created once and in which you modify 1st and 3d items:
// 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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With