Breeze Breeze - 6 months ago 20
Vb.net Question

IEnumerables behaving different when deleting Items from them in a For Each loop

It is commonly known that removing items from an

IEnumerable
inside a For Each loop iterating over it is a really bad idea. I always thought that
IEnumerable
would throw an exception when you still try to do it.

However I noticed that
List
and
TreeNodeCollection
don't behave the same way, even though they both implement
IList
,
ICollection
and
IEnumerable
.

List
behaves as I expected. The following code throws an
InvalidOperationException
:

Dim list As New List(Of String)

list.Add("Entry 1")
list.Add("Entry 2")
list.Add("Entry 3")
list.Add("Entry 4")
list.Add("Entry 5")

For Each entry As String In list
list.Remove(entry)
Next


but
TreeNodeCollection
doesn't throw an exception in .NET 2 and deletes some of the Items, in .NET 4.5.2 the snippet throws a
NullReferenceException
:

Dim tree As New TreeView
Dim root As TreeNode = tree.Nodes.Add("Root")
root.Nodes.Add("Node 1")
root.Nodes.Add("Node 2")
root.Nodes.Add("Node 3")
root.Nodes.Add("Node 4")
root.Nodes.Add("Node 5")

For Each node As TreeNode In root.Nodes
root.Nodes.Remove(node)
Next


Why is there such a different behaviour between two
IEnumerable
s?

Answer

The IEnumerable interface doesn't have anything, either technically or via documentation, to say about modifications. There is nothing contractual that requires a collection that implements the interface to throw an exception if they are modified during enumeration.

It may be that some collections are perfectly capable of continuing enumeration even after modification. I.e. what they enumerate may be "the contents of the collection as it was when enumeration started", or they may offer some other guarantees about items which are added or removed after enumeration starts.