TIP: For Each Gotcha– removing items while cycling

Here’s a little tip to save anyone else trouble.

Let’s say you have an Observer pattern

for each observer in me.myObservers myObserver.Receive(stuff) Next
If in the act of cycling through your observers, you decide to remove an observer from the list, you may not actually talk to each item in the for each list. While you are removing a specific item from the list, it changes the order the For Each is going through and means that another element may end up not getting called. (This is the same as For i = 1 to x as well.)

Instead, try this

[code]// Make a copy of the array
dim copyOfMyObservers() as Observer
for each item as Observer in me.myObservers
copyOfMyObservers.append item
Next

// Cycle through the copy
for each observer in copyOfMyObservers
myObserver.Receive(stuff)
Next[/code]

We don’t use copyOfMyObservers = me.MyObservers because that doesn’t make a copy. It simply creates a reference back to the original property.

The same can be true for any set of values that might change while you’re cycling through them like Window(i). Correct me if I’m not quite doing this right. :slight_smile:

In those cases, I cycle through the array backwards.

Which you cannot obviously do with “FOR EACH”
and I didn’t realize that “FOR EACH” does not necessarily equate to “FOR I=0 to COUNT”, in that the object returned in “FOR EACH” is not in any specific order (as noted in the LR)

That’s what I was thinking too at first but I think the situation is that during the loop that’s broadcasting out to the observers, the observer may choose to remove itself at this time, and then the loops index is off. If you’re looping up you may miss some observers, and looping down you may repeat a message to the same observer.

Using a copy for the loop seems a reasonable way to handle this. A possible edge case is an observer being removed that hasn’t been broadcast to yet. It’s still in the array copy and so gets the message anyways.

I was thinking a linked list might be another way to solve this but can’t quite tell how that’d work out :slight_smile:

Under what circumstance might the loop’s index be thrown off when working backwards? Might Observer 5 remove Observer 1, for example?

Uh, brain fart. yeah, counting down should work even when it’s the observer removing itself. If they’re removing other observers though, not just themselves, then the indexing can get off still.

I suspect cycling backwards is a good idea for most cases.

I think that Will is right in that there could be circumstances where it might not work. For instance, if notifying one observer causes 3 others to remove themselves, would you be in a situation where you get an out of bounds exception in the loop?