C#Dot Net Perls

C#
Collection Was Modified Exception

by Sam Allen

Problem

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.

C# Solution

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();
        }
    }
}
  1. Nodes collection used
    This is a collection of TreeNode objects that are nested in the TreeView.
  2. Foreach loop
    The foreach loop iterates over each item in the Nodes collection.
    (Note that foreach doesn't use any indexer variables in the loop--the compiler handles all that stuff automatically.)
  3. ... and then Remove
    We call the Remove method, and it tries to modify the collection during the loop.

What is wrong with the code?

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()

What does the exception mean?

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.

What does foreach do?

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.

How can we fix it?

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();
    }
}

Conclusion

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.

Dot Net Perls is dedicated to sharing code and knowledge. It has
© 2007-2008 Sam Allen. All rights reserved.

Ads by The Lounge