What Happens When the Data in a "foreach" Loop Changes?

Godot Version

4.4

Question

What happens if the data you are iterating over in a foreach loop changes? For example if you had code like this:

foreach(Item item in AllTheItems)
{
   item.doSomething();
}

And while that loop was running a script somewhere else added or removed an item from “AllTheItems.” What would happen? Does the engine throw an error when “AllTheItems” is modified? Does it only throw an error when the foreach loop tries to access an item that no longer exists? Something different?

Hi!

Doing that is way too risky and you absolutely want to avoid that. You’ll get exceptions for sure, I don’t know if they’ll be thrown only if a modification actually happens or not, but in any way, there’s no good reason of taking the risk. I believe there are compilations warnings telling you that you’re doing something weird (technically working compilation-wise, but conceptually very wrong).

There are safe way of iterating over a collection that may change, like creating a copy of that collection, or using reversed for loops.

Thanks for the advice. Creating a copy of the collection is probably impracticable. I was thinking something like this for replacing foreach loops:

for(int i=0; i < AllTheItems.Count; i++)
{
   if(i >= AllTheItems.Count)
   {
      // Send Error Message
      break;
   }
   
   Item item = AllTheItems[i];

   // rest of code
}

I think this would avoid crashing if AllTheItems changes. I’m not sure if the if statement is necessary. Does anyone know if in C# the for statement will run “AllTheItems.Count” just when the loop is initialized or each iteration?

If you want to be able to delete elements from an array while iterating over it, just iterate backwards.

for (int i = AllTheItems.Count - 1; i >= 0; --i) {
    Item item = AllTheItems[i];
    ....
    AllTheItems.RemoveAt(i);
}

Does anyone know if in C# the for statement will run “AllTheItems.Count” just when the loop is initialized or each iteration?

As the condition is called with each iteration, Count will be run each iteration too (in your code, not in mine). But I’m sure it just uses an internal counter, and doesn’t compute the size of the array each time.

3 Likes
for(int i = AllTheItems.Count ;--i >0;)

for lite shorter and no -1

1 Like

More confusing to quickly grasp though

1 Like

> I'm going to loose the ability to post eventually without good answers

It can’t be done. Because to add items the script requires item.New()
and so does the container AllTheItems.New()
It’s a program memory limit.
Items that are generated by the UI are permanent, unless initially empty.
Something like that, with more imaginary limits.
Items that are generate while the game loads are new items. Also, this has to do with the fact. Each level that loads, Reloads the new items or passes them on to the next level. Anything?