使用foreach遍历Queue时抛出‘Collection was modified’异常求助
问题原因与解决方法
错误根源
你触发的System.InvalidOperationException是因为在foreach遍历Queue集合的过程中直接修改了集合结构(调用Dequeue)。.NET的集合枚举器会在枚举期间检测集合的结构变化(添加、删除元素),一旦检测到就会抛出这个异常,防止枚举结果不一致。
另外你的代码还有个逻辑漏洞:用单个变量ob1X控制所有障碍物的位置,这会导致所有障碍物共享同一个X坐标,移动和移除逻辑完全混乱,每个障碍物必须有独立的位置数据。
正确实现方案
第一步:定义障碍物数据结构
不能只存Texture2D,要创建一个包含纹理和位置的结构体/类,让每个障碍物有自己的坐标:
public struct Obstacle { public Texture2D Texture; public int X; public int Y; // 根据你的游戏地面高度设置固定值 }
第二步:初始化障碍物队列
在LoadContent中初始化带位置的障碍物:
Queue<Obstacle> obstacles = new Queue<Obstacle>(); // 初始X设为屏幕宽度(从右侧进入),Y设为地面高度 obstacles.Enqueue(new Obstacle { Texture = obstacle1, X = GraphicsDevice.Viewport.Width, Y = 400 });
第三步:安全更新障碍物(避免枚举时修改集合)
这里提供两种可靠的实现方式:
方式一:用临时列表处理更新与移除
// 把队列转成临时列表,方便修改 List<Obstacle> tempObstacles = obstacles.ToList(); // 更新所有障碍物的位置 for (int i = 0; i < tempObstacles.Count; i++) { var updated = tempObstacles[i]; updated.X--; // 向左移动 tempObstacles[i] = updated; } // 移除已经完全移出屏幕的障碍物 tempObstacles.RemoveAll(obs => obs.X + obs.Texture.Width <= 0); // 重新赋值给原队列 obstacles = new Queue<Obstacle>(tempObstacles);
方式二:利用Queue先进先出特性,先清理再更新
// 先移除队列头部已经移出屏幕的障碍物(因为Queue是先进先出,最早进入的先移出) while (obstacles.Count > 0) { var firstObs = obstacles.Peek(); if (firstObs.X + firstObs.Texture.Width <= 0) { obstacles.Dequeue(); } else { // 头部元素还在屏幕内,后面的也肯定在,直接跳出循环 break; } } // 更新剩余障碍物的位置 Queue<Obstacle> updatedQueue = new Queue<Obstacle>(); foreach (var obs in obstacles) { var updated = obs; updated.X--; updatedQueue.Enqueue(updated); } obstacles = updatedQueue;
额外说明
- 不要在foreach遍历集合时直接修改集合结构(添加、删除、清空),这是.NET集合的通用规则,不止Queue会触发这个异常。
- 每个障碍物必须维护自己的位置数据,不能用单个变量控制所有元素,否则会出现所有障碍物同步移动、错误移除的问题。
内容的提问来源于stack exchange,提问作者Tiny Designs
相关产品推荐
相关产品推荐

