Collection Was Modified: Enumeration Operation May Not Execute
Introduction
In game development, errors are an inevitable part of the journey. They can be as baffling as frustrating, giving us delays (and headaches). One error that often leaves new game developers scratching their heads is the “Collection was Modified” error. This error crops up all the time in enumeration operations. I still remember the first time I encountered it. This error had me stuck for a while. Then, I googled it. And I was still stuck!
This article will dive deep into this error, understand its causes, and explore practical ways to handle it.
What is the “Collection was Modified” Error?
We will get into the nitty-gritty details of this error. First, let’s break down what it means. This error occurs when you attempt to change a collection in a foreach loop. A collection is a group of related objects. Examples of collections include arrays, lists, stacks, and queues. Arrays and lists are the most common.
In simpler terms, imagine you have a list of items, and you’re trying to loop. At the same time, you’re making changes to the list itself. We have ourselves a recipe for trouble.
The Culprits: What Causes this Error?
1. Simultaneous Enumeration and Modification
There is one primary reason behind this error. You attempted to change a collection while iterating over it in a loop. Whether it’s a c# list, c# dictionary, or any other collection type. You will get this error if you try to change the source collection while iterating it. One common scenario is that you try to add or remove elements in the collection.
2. Simultaneous Modification while Multithreading
The error can also appear in a multithreaded environment. In that environment, more than one thread can access and change a single collection simultaneously. Multithreading is an advanced use case. I won’t address this topic in this blog post.
How to Tackle the Error
So, we understand the origins of the “Collection was Modified” error. Now, let’s explore a practical strategy to handle it.
To avoid this error, create a copy of the collection you’re iterating over. Then, you change the original collection during the enumeration. These changes affect the original but don’t affect the copy you use for the enumeration.
To create a copy, use the ToList method like so:
List<int> myNumber = new List<int>() { 420, 69 };
List<int> ints = myNumber.ToList();
So, here’s an example of some code that will cause the error:
using System.Collections.Generic;
using UnityEngine;
public class CollectionModifiedDemo : MonoBehaviour
{
List<int> myNumber = new List<int>() { 420, 69 };
void Start()
{
foreach (int number in myNumber)
{
myNumber.Remove(number);
}
}
}
As you can see, we iterate the list. Unfortunately, we remove elements from it during the enumeration. As a result, Unity throws the error.
We can use the ToList method to fix this issue to create a new copy of the myNumber list for the foreach loop.
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class CollectionModifiedDemo : MonoBehaviour
{
List<int> myNumber = new List<int>() { 420, 69 };
void Start()
{
foreach (int number in myNumber.ToList())
{
myNumber.Remove(number);
}
}
}
Let’s take a real-world example.
Imagine you are a game developer working on a 2D platformer game. In your game, you have a list of enemies the player character needs to defeat as they progress through the level. You store these enemies in a list collection.
Now, you want to add a new feature to the end of each level. At the end of the level, you drop a power-up item for each enemy the player defeated. How? Simple. You iterate through the list of enemies and spawn power-up items.
Here’s how you might approach this without using an intermediate collection. This approach will cause the error.
foreach (Enemy enemy in enemies)
{
if (enemy.IsDefeated())
{
SpawnPowerUp(enemy.GetPowerUpType());
enemies.Remove(enemy);
}
}
In this scenario, you try to remove a defeated enemy from the collection during each iteration of the foreach statement. When you remove an enemy, you modify the list.
As you know, when you change the list while iterating through it, you will encounter the “Collection was Modified” error.
To avoid this error, you can use an intermediate collection. We can make our middle-man collection on the fly using the ToList() method. Here’s the improved approach.
// Create a copy of the enemies list to avoid modifying it while iterating
foreach (Enemy enemy in enemies.ToList())
{
if (enemy.IsDefeated())
{
SpawnPowerUp(enemy.GetPowerUpType());
enemies.Remove(enemy);
}
}
Creating a copy of the “enemies” list and iterating through the duplicate ensures you don’t modify the original collection. But you still spawn power-up items for the player for the enemies that they defeated. This approach helps you avoid the “Collection was Modified” error.
This strategy is one of many to iterate and modify a list. It is a straightforward solution, and it works for most use cases.
Conclusion
The “Collection was Modified” error can be a real headache if you don’t know how to approach it. But it’s super easy to solve if you do. You can be on your way if you understand one strategy to fix it.