Remove an object when in a for each loop

12,864

Solution 1

You cannot delete an object from a collection during enumeration. You cannot modify the collection at all. That will cause an error (Collection was modified; enumeration operation may not execute). But you could add the objects that you want to delete/remove to another collection:

Dim removeEnemies = New List(Of enemy)
For Each enemy As enemy In lstEnemy
    ' ... '
    If enemy.enemy.Left < 0 Then
        removeEnemies.Add(enemy.enemy)
    End If
Next

For Each enemy In removeEnemies
    lstEnemy.Remove(enemy)
    Me.Controls.Remove(enemy.enemy)
Next

All of the following methods will cause a list to change its version (which is checked during enumeration):

  • Add
  • Clear
  • Insert
  • InsertRange
  • Remove
  • RemoveRange
  • RemoveAt
  • Reverse
  • [the Indexer setter]
  • Sort

Another option is to use a For-Loop and loop it backwards:

 For i As Int32 = lstEnemy.Count - 1 To 0 Step -1
    Dim enemy = lstEnemy(i)
    ' ... '
    If enemy.enemy.Left < 0 Then
        lstEnemy.Remove(enemy)
        Me.Controls.Remove(enemy.enemy)
    End If
Next

This will not raise that error but it's not as readable. You need to go from list.Count - 1 To 0 because you want to remove items which would change the Count property and an index that was available before the item was removed causes now an ArgumentOutOfRangeException.

Last but not least, you can use List.RemoveAll:

lstEnemy.RemoveAll(Function(enemy) enemy.enemy.Left < 0)

Solution 2

.NET really doesn't like it when you change a collection when you're in the middle of enumerating its contents. You might try chAnging your foreach loop to a for loop if you're planning on removing elements from the collection like this.

Share:
12,864
Tyler Hilbert
Author by

Tyler Hilbert

Updated on June 23, 2022

Comments

  • Tyler Hilbert
    Tyler Hilbert almost 2 years

    I'm making a survival game and trying to remove an object when it goes off the screen. Here is the code:

    Public Sub tmrEnemyMove_Tick(sender As Object, e As EventArgs) Handles tmrEnemyMove.Tick
        Dim koopaAnimation As Boolean
    
        For Each enemy As enemy In lstEnemy
            enemy.enemy.Left = enemy.enemy.Left - 20
    
            If enemy.enemy.Tag = "koopa" Then
                enemy.enemy.Image = Image.FromFile(Application.StartupPath + "\Graphics\koopa" + Trim(Str(koopaPosition)) + ".png")
                If koopaAnimation = False Then
                    If koopaPosition = 0 Then
                        koopaPosition = 1
                    Else
                        koopaPosition = 0
                    End If
                End If
                koopaAnimation = True
            End If
    
            If picMario.Left < enemy.enemy.Left AndAlso enemy.enemy.Left < picMario.Right Or picMario.Left < enemy.enemy.Right AndAlso enemy.enemy.Right < picMario.Right Then
                If picMario.Top < enemy.enemy.Top AndAlso enemy.enemy.Top < picMario.Bottom Or picMario.Top < enemy.enemy.Bottom AndAlso enemy.enemy.Bottom < picMario.Bottom Then
                    'MsgBox("Collision")
                End If
            End If
    
            If enemy.enemy.Left < 0 Then
                lstEnemy.Remove(enemy)
                Me.Controls.Remove(enemy.enemy)
            End If
        Next
    End Sub
    

    The error I get is: An unhandled exception of type 'System.InvalidOperationException' occurred in mscorlib.dll Additional information: Collection was modified; enumeration operation may not execute.

    If anyone could help that would be great, thanks.

  • Guillermo Varini
    Guillermo Varini over 6 years
    other option is to use a while. great answer