Modifying a Collection While Iterating Over It

I was recently bitten by a bug in some code I had modified. The error message was:

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.

Here is the offending code:

PropertyValueCollection existingProxyAddresses = ADEntry.Properties["proxyAddresses"];
foreach (string proxy in existingProxyAddresses) {
    if (proxy.StartsWith("smpt:")) {
        ADEntry.Properties["proxyAddresses"].Remove(proxy);
    }
}

A little head scratching preceded the “Ah ha!” moment when I realized that the initial assignment of the proxyAddresses property to existingProxyAddresses was a simple alias, it was not a copy of the collection. The fix of course was to iterate over a copy of the collection.

The fact that this is AD code is not really not relevant except that the ProperyValueCollection type is a subclass of a collection primitive that doesn’t support LINQ. I would have preferred to used a LINQ extension to create the copy of the collection. PropertyValueCollection does have a CopyTo method so I used that instead but that requires pre-creating the target collection thus:

var existingProxyAddresses = new object[ADEntry.Properties[AdAttributes.ProxyAddress].Count];
ADEntry.Properties[AdAttributes.ProxyAddress].CopyTo(existingProxyAddresses, 0);

Note that the CopyTo method expects an array as the target type. Also note that although I show a string for the property name in the first code snippet, I am actually using a string constant in a class I’ve declared for that purpose.

So why can’t you modify a collection while you are iterating over it? I don’t know the formal reason but I suspect it is because a collection has no defined order. Because of this lack of ordering, the IEnumerator.MoveNext() method, which is used by the foreach statement, won’t have a defined meaning if elements are removed from the collection during the enumeration. As an example, suppose the foreach enumeration was coded to internally copy the collection and iterate over that copy. If its internal order was A, B, C, D and you removed C from the underlying collection, what would happen if the enumerator went to C? It would be undefined. Thus you are prevented from doing this.

 

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.