【问题标题】:Can you remove an item from a List<> whilst iterating through it in C#在 C# 中迭代​​时,您可以从 List<> 中删除一个项目吗?
【发布时间】:2010-12-05 05:37:51
【问题描述】:

你可以在迭代时从列表中删除一个项目吗?这会起作用吗,还是有更好的方法来做到这一点?

我的代码:

foreach (var bullet in bullets)
  {
    if (bullet.Offscreen())
    {
      bullets.Remove(bullet);
    }
  }

-edit- 抱歉各位,这是给 Silverlight 游戏的。我没有意识到 silverlight 与 Compact Framework 不同。

【问题讨论】:

标签: c# .net silverlight list iteration


【解决方案1】:
bullets.RemoveAll(bullet => bullet.Offscreen());

编辑:要在 Silverlight 中按原样工作,请将以下扩展方法添加到您的项目中。

List&lt;T&gt;.RemoveAll 一样,此算法是 O(N),其中 N 是列表的长度,而不是 O(N*M),其中 M 是从列表中删除的元素数。由于它是一种扩展方法,其原型与非 Silverlight 框架中的 RemoveAll 方法具有相同的原型,因此将在可用时使用内置的方法,并且此方法可无缝用于 silverlight 构建。

public static class ListExtensions
{
    public static int RemoveAll<T>(this List<T> list, Predicate<T> match)
    {
        if (list == null)
            throw new NullReferenceException();

        if (match == null)
            throw new ArgumentNullException("match");

        int i = 0;
        int j = 0;

        for (i = 0; i < list.Count; i++)
        {
            if (!match(list[i]))
            {
                if (i != j)
                    list[j] = list[i];

                j++;
            }
        }

        int removed = i - j;
        if (removed > 0)
            list.RemoveRange(list.Count - removed, removed);

        return removed;
    }
}

【讨论】:

  • 听起来不错,但 removeall 对我来说并不作为函数存在(silverlight),即使使用“使用 system.linq” - 我不确定有什么问题?
  • @Benjol:很高兴看到其他实现目标的方法。有时其他方法也有意义。例如,反向 for 循环模式对于根本不应用 RemoveAll 方法的数组和 XML 文档非常有用。在修改现有集合时,它在这些场景中也很有帮助。
  • 我发现有人问为什么 removeall 在较新版本中从 C# 中删除:forums.silverlight.net/forums/t/19099.aspx Strange???
  • OK removeall 不在 Silverlight 中。无论如何,这仍然是一个很好的解决方案,值得为其他项目记住!
  • @Chris:已修复以解决问题,通过扩展方法您可以按原样使用代码,它将无缝支持多个框架。
【解决方案2】:

编辑:澄清一下,问题是关于Silverlight,它显然不支持RemoveAll on List`T。它在full framework, CF, XNA versions 2.0+中可用

您可以编写一个表达您的删除标准的 lambda:

bullets.RemoveAll(bullet => bullet.Offscreen());

或者您可以选择您想要的,而不是删除您不想要的:

bullets = bullets.Where(b => !b.OffScreen()).ToList();

或者使用索引器在序列中向后移动:

for(int i=bullets.Count-1;i>=0;i--)
{
    if(bullets[i].OffScreen())
    {
        bullets.RemoveAt(i);
    }
}

【讨论】:

  • 这与List&lt;T&gt;.RemoveAll相比效率很低(见我的回答)。
  • @280z28 啊,是的,忘了 RemoveAll。
  • 你的第二个版本最适合我(反向迭代)。 RemoveAll 似乎不再起作用(已弃用?)。
  • @Chris RemoveAll 绝对有效。见msdn.microsoft.com/en-us/library/wdka673a.aspx
  • @Chris 好吧,它已从 Silverlight 库中删除。它适用于所有其他版本。
【解决方案3】:

尝试在 foreach 循环中删除它会引发异常。您需要使用 for 循环向后迭代它。

for (int count = bullets.Count - 1; count >= 0; count--)
{
  if (bullets[count].Offscreen())
    {
        //bullets.Remove(bullets[count]);
        bullets.RemoveAt(count);
    }
}

【讨论】:

  • 这看起来是个好主意,谢谢。向后遍历您认为的列表不会有性能损失?
  • for 循环通常被认为是快速的。反向遍历它与正常遍历它应该没有太大不同。此外,没有临时列表复制,因此会影响原始集合。
  • 另外,我认为'removeat'会比'remove'更快
  • @Chris:同意,这是一个更好的选择,因为索引是已知的。
【解决方案4】:

试试这个:

bullets.RemoveAll(bullet => bullet.Offscreen());

【讨论】:

    【解决方案5】:

    最好创建一个包含要删除的项目的列表,然后从列表中删除项目:

    List<Bullet> removedBullets = new List<Bullet>();
    
    foreach(var bullet in bullets)
    {
      if (bullet.OffScreen())
      {
       removedBullets.Add(bullet);
      }
    }
    
    foreach(var bullet in removedBullets)
    {
      bullets.Remove(bullet);
    }
    

    【讨论】:

    • 这是我最初的想法,但由于它是一款实时游戏,由于可能的开销,我并不热衷于创建新列表。
    【解决方案6】:

    在“for”循环中迭代,而不是通过 foreach 迭代。这将起作用。

    【讨论】:

      【解决方案7】:

      I've come across this problem before and blogged about it here.

      短版是你可以创建一个名为 RemoveIf 的扩展方法:

      public void RemoveIf<T>(ICollection<T> collection, Predicate<T> match)
      {
          List<T> removed = new List<T>();
          foreach (T item in collection)
          {
              if (match(item))
              {
                  removed.Add(item); 
              }
          }
      
          foreach (T item in removed)
          {
              collection.Remove(item);
          }
      
          removed.Clear();
      }
      

      然后在每次需要时与您的代表一起调用它:

      RemoveIf(_Entities.Item, delegate(Item i) { return i.OffScreen(); });
      

      【讨论】:

        猜你喜欢
        • 2010-10-10
        • 2023-02-12
        • 2011-11-12
        • 2014-01-05
        • 2010-12-01
        • 2019-03-10
        相关资源
        最近更新 更多