Update:
Daniel Moth may have put this a little more concisely on his blog. I'm not sure I subscribe to his resolution which he admits is a hack. As he says though, "The real solution is not to remove while iterating; it is bad design."
Instead, iterate through collecting the keys to remove. Then iterate through your list of keys and remove the associated object from the collection. This is what I tried to illustrate in my "Good Remove" method below.
Thanks for the e-mail Daniel. I will look into the posting issue on my blog.
--chaz
********************************************************
I don't know if the same concept exists in C#, but as of late, I have been running into a lot of trouble with using “RemoveAt” with collections in VB. If you iterate through a collection in ascending order, you may windup removing an item from the list at an early (0, 1, etc.) index. This causes all items in the collection to move up one index. The problem is that your loop doesn't know this happened. The end result... you guessed it, index out of range.
Following is a simple piece of code that illustrates the remove loop. Below it is the collection class.
Imports
System
Namespace JCL_TS.Example.Collections
Public Class Example
Private myCollection As New JCL_TS.Example.Collections.SampleCollection
Public Sub BadRemove()
For index As Int32 = 0 To MyCollection.Count - 1
myCollection.RemoveAt(index)
Next
End Sub
End Class
End Namespace
Imports
System
Imports System.Collections
Namespace
JCL_TS.Example.Collections
Public Class SampleCollection
Inherits CollectionBase
Public Function Item(ByVal index As Int32) As String
Return CType(List.Item(index), System.String)
End Function
Public Shadows Sub RemoveAt(ByVal index As Int32)
List.RemoveAt(index)
End Sub
End Class
End
Namespace
There are many ways around this, like removing a specific key rather than an index. For example if I change the collection above to have the following method...
Public Sub Remove(ByVal key As String)
List.Remove(key)
End Sub
Then I would replace the “BadRemove” method in the “Example” with one that first iterates through the collection stashing the matches in an array. Once I have my matches, I iterate through the array of matches removing the items from the collection accordingly.
Public Sub GoodRemove(ByVal myKey As String)
Dim keyArray() As String
Dim counter As Int32 = -1
For index As Int32 = 0 To myCollection.Count - 1
If CType(myCollection.Item(index), System.String) = myKey Then
counter += 1
ReDim keyArray(counter)
keyArray(counter) = myKey
End If
Next
For keyIndex As Int32 = 0 To keyArray.GetUpperBound(0)
myCollection.Remove(CType(keyArray(keyIndex), System.String))
Next
End Sub
I'm sure that are a number of ways to skin this cat, and I'm sure a number of them are a lot more efficient than my example. But this works for a quick illustration. This is the first time I am including a code example in my post. Let me know if it is helpful or if it needs improvement.
--chaz