You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 22:53:06