2

我想删除所有将删除布尔值设置为 true 的硬币,并且我知道我无法从 foreach 正在迭代的同一个集合中删除它。所以我做了一个副本(临时),但它一直抛出同样的异常:

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

我究竟做错了什么?这是我的代码:

List<Coin> temp = coins;
foreach (Coin c in coins)
{
    if (c.delete)
        temp.Remove(c);
    else
        c.somethingElse();
}
coins = temp;
4

3 回答 3

1

我究竟做错了什么?

您的代码不起作用的原因是因为您只是将引用复制到硬币变量。就像 C 中的指针一样,引用只不过是一个保存另一种类型的内存地址的变量。所以 temp 变量指向与 coin 变量完全相同的集合。

所以我做了一个副本(临时)

您的代码打算做什么(如您所描述的)是这样的:

List<Coin> temp = coins.ToList();
foreach (Coin c in coins)
{
    if (c.delete)
        temp.Remove(c);
    else
        c.somethingElse();
}
coins = temp;

(见Enumerable.ToList

正如 Dax 已经提到的,还有其他更短的方法可以达到相同的结果。在这种情况下,List.RemoveAll 是一个完全可以接受的解决方案。您可能还想查看LINQ 查询

于 2013-07-25T01:20:53.143 回答
1

这里最简单的做法是调用coins.RemoveAll(coin => coin.delete);. (谷歌List.RemoveAll)。这会给您留下一个硬币列表,在一个语句中删除了所有“删除”,无需担心计数器或循环变量或临时副本或其他任何东西。因此,您可以随心所欲地迭代剩余的硬币。

coins.RemoveAll(coin => coin.delete);
foreach (var coin in coins)
    coin.somethingElse();

如果您需要一个只遍历列表一次的更快的实现,这就是您所需要的。不要在循环中RemoveRemoveAt循环中使用任何解决方案,因为这些操作很慢,因此在循环中使用它们会降低性能。下面只会将每个“好”硬币向下移动到该index位置然后递增index,因此任何时候所有“好”硬币都在下方index。最后你把上面所有的硬币都拿掉了index,所以你只剩下“好”的硬币了。由于它只迭代一次,它比RemoveAll选项快大约 2 倍,并且比任何解决方案或循环中的方法都要快。RemoveRemoveAt

var index = 0;
for (var i = 0; i < coins.Count; ++i)
{
    var coin = coins[i];
    if (!coin.delete)
    {
        coin.somethingElse();
        coins[index++] = coin;
    }
}
coins.RemoveRange(index, coins.Count - index);
于 2013-07-25T00:29:49.323 回答
0

由于您使用“xna”和“monogame”标记了您的问题,因此您可能会受益于避免产生垃圾的解决方案(堆内存分配将触发后续垃圾收集器搅动)。这是我要使用的,它也适用于任何具有列表类型数据结构的编程语言,即使 LINQ 的魔力不可用:

// Iterate the list backwards so we never skip items or accidentally
// access past the end of the list.
for (int i = coins.Count - 1; i >= 0; --i)
{
   if (coins[i].delete)
   {
      coins.RemoveAt(i);
   }
   else
   {
      coins[i].somethingElse();
   }
}

在@DaxFohl 的解决方案中,我相信创建 lambda(“coin => coin.delete”)将在堆上分配垃圾内存。并且枚举也有可能从堆中分配,至少在 Mono 中是这样。我听说微软在 Windows 上的规范 .NET 实现对这些枚举进行了超级优化的 wrt 内存使用,但我喜欢在我自己的游戏逻辑中安全地玩它。

另一方面,如果这不是您的逻辑的内部循环样本(例如,在 Update 方法中非常频繁地调用),那么如果这里生成了一点垃圾,这可能并不重要。

于 2013-07-28T02:09:59.890 回答