Вопрос

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

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

Некрасиво, но это должно сработать.

Это было полезно?

Решение

Вам действительно нужно сделать это в цикле foreach ?

Это приведет к тем же результатам, что и ваши примеры, т.е. удалит все элементы из списка вплоть до первого элемента, соответствующего условию (или удалит все элементы, если ни один из них не соответствует условию).

int index = Os.FindIndex(x => x.cond);

if (index > 0)
    Os.RemoveRange(0, index);
else if (index == -1)
    Os.Clear();

Другие советы

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

for (int i = myList.Count - 1; i >= 0; i--)
{
    if (whatever) myList.RemoveAt(i);
}
<Ч>

В ответ на ваш комментарий о желании выйти, когда вы обнаружите элемент, который НЕ удаляете, лучшим решением будет использование цикла while.

Вы никогда не должны удалять что-либо из коллекции, которую вы повторяете внутри цикла foreach. Это в основном как пилить ветку, на которой ты сидишь.

Используйте альтернативу while. Это путь.

Я программист на Java, но что-то вроде этого работает:

List<Type> Os;
List<Type> Temp;
...
foreach (Type o in Os)
    if (o.cond)
        Temp.add(o);
Os.removeAll(Temp);  

У меня только что была эта проблема с моей библиотекой анализа. Я попробовал это:

for (int i = 0; i < list.Count; i++)
{                
   if (/*condition*/)
   {
       list.RemoveAt(i);
       i--;
   }
}

Это довольно просто, но я не думал ни о каком переломном моменте.

Вот САМОЕ ПРОСТОЕ РЕШЕНИЕ с самым простым ПОЧЕМУ

Проблема:

Как правило, мы удаляем из исходного списка, это создает проблему поддержания счетчика списка и местоположения итератора.

List<Type> Os = ....;
Os.ForEach(
    delegate(Type o) {
        if(!o.cond) Os.Remove(o);
    }
);

РЕШЕНИЕ - LINQ.ForEach :

Обратите внимание, все, что я добавил, было ToList () . Это создает новый список, для которого вы выполняете ForEach, поэтому вы можете удалить свой оригинальный список, продолжая при этом перебирать весь список.

List<Type> Os = ....;
Os.ToList().ForEach(
    delegate(Type o) {
        if(!o.cond) Os.Remove(o);
    }
);

РЕШЕНИЕ - Обычный foreach :

Этот метод также работает для регулярных foreach .

List<Type> Os = ....;
foreach(Type o in Os.ToList()) {
  if(!o.cond) Os.Remove(o);
}

Обратите внимание, что это решение не будет работать, если ваш исходный список содержит элемент struct .

Я знаю, что вы просили что-то еще, но если вы хотите условно удалить группу элементов, вы можете использовать лямбда-выражение:

Os.RemoveAll(o => !o.cond);
 Os.RemoveAll(delegate(int x) { return /// });

Я бы попытался найти индекс первого элемента, который не удовлетворяет предикату, и выполнить для него RemoveRange (0, index). Если ничего другого, должно быть меньше Удалить звонки.

Обновить:Добавлено для полноты картины

Как уже ответили некоторые, вы не должны изменять коллекцию при повторении ее с помощью GetEnumerator() (пример foreach).Фреймворк не позволяет вам сделать это, создавая исключение.Общим решением для этого является повторение "вручную" с помощью for (смотрите другие ответы).Будьте осторожны со своим индексом, чтобы не пропускать элементы и не переоценивать один и тот же дважды (используя i-- или повторение в обратном направлении).

Однако для вашего конкретного случая мы можем оптимизировать операции удаления...оригинальный ответ приведен ниже.


Если вы хотите удалить все элементы до тех пор, пока один из них не выполнит заданное условие (это то, что делает ваш код), вы можете сделать это:

bool exitCondition;

while(list.Count > 0 && !(exitCondition = list[0].Condition))
   list.RemoveAt(0);

Или, если вы хотите использовать одну операцию удаления:

SomeType exitCondition;
int index = list.FindIndex(i => i.Condition);

if(index < 0)
    list.Clear();
else
{
    exitCondition = list[0].State;
    list.RemoveRange(0, count);
}

Примечание:поскольку я предполагаю, что item.Condition является bool, Я использую item.State чтобы сохранить условие выхода.

Обновить:добавлена проверка границ и сохранение условия выхода в обоих примерах

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

MyList = MyList.Where(x=>(someCondition(x)==true)).ToList()

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

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

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

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

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

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

Об этом подробно рассказывается в Удаление элементов в списке во время итерации по нему .

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

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

    list.Remove(elementToRemove);
}

Решение 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--;
    }
}

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

foreach (введите o in (new List (Os))) {     если что-то)         Os.Remove (о); }

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

Добавьте элемент для удаления в список, а затем удалите эти элементы с помощью 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));
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top