Можете ли вы удалить элемент из списка <>, повторяя его в C # - PullRequest
4 голосов
/ 09 октября 2009

Можете ли вы удалить элемент из списка <> во время его итерации? Будет ли это работать, или есть лучший способ сделать это?

Мой код:

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

-edit- Извините, ребята, это для игры Silverlight. Я не знал, что silverlight отличается от Compact Framework.

Ответы [ 7 ]

17 голосов
/ 09 октября 2009
bullets.RemoveAll(bullet => bullet.Offscreen());

Редактировать: Чтобы сделать эту работу как есть в Silverlight, добавьте следующий метод расширения в ваш проект.

Как и List<T>.RemoveAll, этот алгоритм имеет O (N), где N - длина списка, а не O (N * M), где M - количество элементов, удаленных из списка. Поскольку это метод расширения с тем же прототипом, что и в методе RemoveAll, который используется в средах, отличных от Silverlight, встроенный будет использоваться при его наличии, и этот метод будет беспрепятственно использоваться для сборок 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;
    }
}
10 голосов
/ 09 октября 2009

Редактировать : чтобы уточнить, вопрос касается Silverlight , который, очевидно, не поддерживает RemoveAll on List`T . Он доступен в полной версии , версиях CF, XNA 2.0 +

Вы можете написать лямбду, которая выражает ваши критерии удаления:

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);
    }
}
4 голосов
/ 09 октября 2009

Попытка удалить его в цикле foreach вызовет исключение. Вам нужно перебрать его в обратном направлении с помощью цикла for.

for (int count = bullets.Count - 1; count >= 0; count--)
{
  if (bullets[count].Offscreen())
    {
        //bullets.Remove(bullets[count]);
        bullets.RemoveAt(count);
    }
}
3 голосов
/ 09 октября 2009

Попробуйте это:

bullets.RemoveAll(bullet => bullet.Offscreen());
2 голосов
/ 09 октября 2009

Итерация в цикле "for", а не итерация в foreach. Это будет работать.

2 голосов
/ 09 октября 2009

Лучше создать список, который будет содержать элементы для удаления, а затем удалить элементы из списка:

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);
}
1 голос
/ 09 октября 2009

Я сталкивался с этой проблемой раньше и писал об этом здесь .

Короткая версия - вы можете создать метод расширения с именем 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(); });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...