修改了集合,其中未修改项目

时间:2012-12-31 12:20:46

标签: c#

我正在使用XNA库为C#开发一个简单的游戏。在下面的代码片段中,我得到了

  

收藏被修改;枚举操作可能无法执行。

第二个foreach循环顶部的

错误。在我(相对有限的)C#体验中,这在尝试在循环期间修改底层集合时会发生。但是,据我所知,我没有以任何方式修改enemy_positions集合。此代码中的所有集合都属于List<Vector2>类型。

这里发生了什么?

//defines collision behaviour when enemy is hit
int be_no = 0;
List<Vector2> tmp_bullets = bullet_i_position;
List<Vector2> tmp_enemy = enemy_positions;

foreach (Vector2 bullet in bullet_i_position)
{
    //get bullet collision box
    Rectangle bullet_col = new Rectangle(Convert.ToInt32(bullet.X - 12), Convert.ToInt32(bullet.Y - 12), 25, 26);

    int en_no = 0;

    foreach (Vector2 enemy in enemy_positions)
    {
        //get enemy collsion box
        en_box = new Rectangle(Convert.ToInt32(enemy.X), Convert.ToInt32(enemy.Y), 75, 75);

        if (temp_r.Intersects(en_box))
        {
            //remove all colliding elements
            tmp_enemy.RemoveAt(en_no);
            tmp_bullets.RemoveAt(be_no);
            bullet_direction.RemoveAt(be_no);

        }
        en_no++;
    }
    be_no++;
}

//update actual lists
bullet_i_position = tmp_bullets;
enemy_positions = tmp_enemy;

3 个答案:

答案 0 :(得分:6)

行:

List<Vector2> tmp_bullets = bullet_i_position;
List<Vector2> tmp_enemy = enemy_positions;

不是克隆列表,它们只是创建对同一列表的本地引用。直接解决方案是将这两行更改为:

List<Vector2> tmp_bullets = new List<Vector2>(bullet_i_position);
List<Vector2> tmp_enemy = new List<Vector2>(enemy_positions);

但是这会在每次调用方法时分配一个新的列表,这对于垃圾收集(特别是在游戏中)来说会很糟糕,所以更好的解决方案是删除你的foreach循环并用反向替换它们循环。这是有效的,因为在使用枚举器迭代集合时,您只会获得该异常。同样的问题不适用于常规for循环。例如:

for (int i = bullet_i_position.Count - 1; i >= 0; i--)
{
    Vector2 bullet = bullet_i_position[i];

    // ...
}

反向迭代的原因还在于,在定期迭代时删除元素意味着您将在删除元素之后跳过元素(因为索引向下移动1)

答案 1 :(得分:0)

List<Vector2> tmp_enemy = enemy_positions;

不制作enemy_positions的副本并将其分配给tmp_enemy。相反,tmp_enemy指向enemy_positions。因此,tmp_enemy中的任何更改都会反映在enemy_positions中。 如果你想制作一个实际的副本,这是一个更好的方法:

List<Vector2> tmp_enemy = new List<Vector2>(enemy_positions);

答案 2 :(得分:0)

正如其他人所说,你遇到了参考和价值类型之间的区别

我建议你用类似下面的东西替换foreach循环

for(int i = 0; i<bullet_i_position.count;i++)
  {
     bool hascollided = false;
     for(int j = 0; j<enemy_positions.count;j++)
        {
          if(collisionOccurs)
               {
                hasCollided = true;
                enemy_positions.RemoveAt(j);
                j--;
               }
       }
   if(hasCollided)
      {
         bullet_i_position.RemoveAt(i);
         i--;
      }
}

你需要考虑如果子弹在上面的例子中同时与2个敌人发生碰撞会发生什么情况它会同时取消