You may get the "collection was modified" exception in your program, but what does it mean? You have a collection such as a TreeView, and you want to remove items such as TreeNode controls in a loop. You are using a foreach loop and want to remove items in the collection while iterating over it.
This article has solutions and observations about this exception. We need to examine some situations that can cause this exception, and then some ways to fix it--and whether we should fix it. The example below demonstrates a TreeNodeCollection and a foreach loop that tries to remove an item, but raises an exception.
/// <summary>
/// Shows the error-causing way of removing items from a TreeView.
/// </summary>
private void ExampleBad()
{
// We try to remove all the tree nodes with no children, but this is bad
// because we are modifying the Nodes collection while using foreach on it.
// Don't use this code.
foreach (TreeNode treeNode in treeView1.Nodes) // Uses TreeNodeCollection
{
if (treeNode.Nodes.Count == 0)
{
treeNode.Remove();
}
}
}
Running the code will produce an error like the following. This is an exception, which is the same as a crash basically, but which we can also handle more gracefully in code. However, in this specific case, we shouldn't try to handle the exception, but should fix our code.
System.InvalidOperationException was unhandled
Message="Collection was modified; enumeration operation may not execute."
Source="mscorlib"
StackTrace:
at System.ThrowHelper.ThrowInvalidOperationException(...)
at System.Collections.Generic.List`1.Enumerator.MoveNext()
All the InvalidOperationException means is that your code does something bad. The Message on the second line is the secret we need to know. It says "Collection was modified" and that the enumeration won't work. This is because we are changing the elements in the collection while looping over it with foreach.
Foreach simply queries the enumerator that belongs to the List in the example and asks for the next element. However, in our example, the enumerator's state becomes invalid when we remove the item from the collection. Remember, an enumerator has to store some data indicating where it currently is.
This problem often occurs in Windows Forms control collections. I was puzzled by it when trying to remove nodes from a TreeView control. What follows is code that accomplished the goal in the first example, without raising an exception.
/// <summary>
/// Shows the correct method of removing items from a TreeView.
/// </summary>
private void ExampleGood()
{
// This example works. We get a separate list of tree nodes we
// want to delete. Then, we delete them in a separate pass.
// Might not be ideal, but it works well.
List<TreeNode> treeList = new List<TreeNode>();
foreach (TreeNode treeNode in treeView1.Nodes)
{
if (treeNode.Nodes.Count == 0)
{
treeList.Add(treeNode);
}
}
// Remove the nodes in a loop that doesn't use the Nodes
// collection directly.
foreach (TreeNode treeNode in treeList)
{
treeNode.Remove();
}
}
To remove items from a collection, such as List or TreeNodeCollection, first add references to all the objects you want to remove in a new List. Then, remove those items when iterating over that temporary collection. This avoids any "Collection not modified" errors, and results in very easy to read and functional code.