.NET - Удалить из списка <T>внутри цикла 'foreach' - PullRequest
46 голосов
/ 07 мая 2009

У меня есть код, который я хочу выглядеть следующим образом:

List<Type> Os;

...

foreach (Type o in Os)
    if (o.cond)
        return;  // Quitting early is important for my case!
    else
        Os.Remove(o);

... // Other code

Это не работает, потому что вы не можете удалить из списка, когда находитесь внутри цикла foreach над этим списком:

Есть ли общий способ решения проблемы?

При необходимости я могу переключиться на другой тип.

Вариант 2:

List<Type> Os;

...

while (Os.Count != 0)
     if (Os[0].cond)
         return;
     else
         Os.RemoveAt(0);

... // Other code

Ужасно, но это должно сработать.

Ответы [ 17 ]

2 голосов
/ 12 марта 2013

вы можете сделать это с помощью linq

MyList = MyList.Where(x=>(someCondition(x)==true)).ToList()
1 голос
/ 06 июня 2009

Посмотрите на Enumerable.SkipWhile()

Enumerable.SkipWhile( x => condition).ToList()

Как правило, не изменяет список, делает жизнь намного проще. :)

1 голос
/ 07 мая 2009

Если вы знаете, что ваш список не очень большой, вы можете использовать

foreach (Type o in new List<Type>(Os))
    ....

, который создаст временную копию списка. Ваш вызов remove () не будет мешать итератору.

1 голос
/ 29 ноября 2013

Решение Anzurio, вероятно, самое простое, но вот еще одно простое решение, если вы не возражаете добавить несколько интерфейсов / классов в свою библиотеку утилит.

Вы можете написать это так

List<Type> Os;
...
var en = Os.GetRemovableEnumerator();
while (en.MoveNext())
{
    if (en.Current.Cond)
        en.Remove();
}

Поместите следующую инфраструктуру, вдохновленную Java Iterator<T>.remove, в вашу служебную библиотеку:

static class Extensions
{
    public static IRemovableEnumerator<T> GetRemovableEnumerator<T>(this IList<T> l)
    {
        return new ListRemovableEnumerator<T>(l);
    }
}

interface IRemovableEnumerator<T> : IEnumerator<T>
{
    void Remove();
}

class ListRemovableEnumerator<T> : IRemovableEnumerator<T>
{
    private readonly IList<T> _list;
    private int _count;
    private int _index;
    public ListRemovableEnumerator(IList<T> list)
    {
        _list = list;
        _count = list.Count;
        _index = -1;
    }

    private void ThrowOnModification()
    {
        if (_list.Count != _count)
            throw new InvalidOperationException("List was modified after creation of enumerator");
    }
    public void Dispose()
    {
    }

    public bool MoveNext()
    {
        ThrowOnModification();
        if (_index + 1 == _count)
            return false;
        _index++;
        return true;
    }

    public void Reset()
    {
        ThrowOnModification();
        _index = -1;
    }

    object IEnumerator.Current
    {
        get { return Current; }
    }

    public T Current
    {
        get { return _list[_index]; }
    }

    public void Remove()
    {
        ThrowOnModification();
        _list.RemoveAt(_index);
        _index--;
        _count--;
    }
}
1 голос
/ 07 мая 2009

Это хорошо обсуждается в Удаление элементов в списке при его повторении .

Они предлагают:

for(int i = 0; i < count; i++)
{
    int elementToRemove = list.Find(<Predicate to find the element>);

    list.Remove(elementToRemove);
}
0 голосов
/ 24 мая 2017

Добавьте элемент для удаления в список, а затем удалите эти элементы с помощью RemoveAll:

List<Type> Os;
List<Type> OsToRemove=new List<Type>();
...
foreach (Type o in Os){
    if (o.cond)
        return;
    else
        OsToRemove.Add(o);
}
Os.RemoveAll(o => OsToRemove.Contains(o));
0 голосов
/ 05 июня 2014

У меня была такая же проблема, и я решил ее с помощью следующего:

foreach (Type o in (new List(Os))) { if (something) Os.Remove(o); }

Перебирает копию списка и удаляет из исходного списка.

...