Quirks Of Using For Each
Collections can be tricky sometimes when you're writing code. This is especially true when you need to modify them while you're looping through them. Let's look at a couple examples that can get you into trouble.
//create a typical list of items List<string> items = new List<string> { "first", "second", "third", "fourth"; }; //now clear the list foreach (string item in items) { items.Remove(item); }
Of course, this won't work. Why?
InvalidOperationException: Collection was modified; enumeration operation may not execute
Once our loop has started the boundaries of the collection have been defined. Adding or removing items will change those boundaries and create errors. Naturally, most languages aren't going to like this and will crash.
Fortunately, we do get an Exception message (which is the best kind of error message). However, we should be more concerned about subtle problems that could arise if we change a collection while looping through it. For example, consider the ForEach
method attached to Generic Lists...
//get a list of the employees that //should be deleted from the system List<string> firedEmployees = new List<string> { "jeff", "blake", "steve" }; //and delete them firedEmployees.ForEach(employee => { EmployeeSystem.Delete(employee); firedEmployees.Remove(employee); });
Everyone deleted? Nope...
I don't know about you, but I was surprised to find that one employee remained on this list after the code was run (the second employee at that). ForEach made sure to keep track of our collection as it changed but unfortunately it created an undesirable result.
To take it a step further, if you were to add a new item each time then you'd end up with an infinite loop, an error that would be caught sooner but a problem all the same.
This straight forward code has potential for some serious problems.
So, abandon the framework! Move to Java! .NET is doomed!
...Or we can just write an extension method to take care of both problems at the same time.
public static class EnumerableExtensions { /// <summary> /// Safely enumerates through a collection /// </summary> public static IEnumerable<T> Each<T>(this IEnumerable<T> collection, Action<T> action) { //move the collection into its own list and //perform the work on each item collection.ToList().ForEach(action); return collection; } }
The fix itself isn't very impressive. One additional call to ToList()
and we're pretty much okay. But this does keep the original collection safe while we enumerate through the protected one.
It is worth mentioning there is more 'behind the scenes' stuff happening than is explained.
July 22, 2010
Quirks Of Using For Each
Post titled "Quirks Of Using For Each"