Проблемы с удалением элементов из списка при повторении в списке

У меня есть цикл, который выполняет итерацию через элементы в списке. Мне нужно удалить элементы из этого списка в цикле на основе определенных условий. Когда я пытаюсь сделать это на С#, я получаю исключение. по-видимому, не разрешено удалять элементы из списка, который выполняется итерацией. Проблема наблюдалась с петлей foreach. Есть ли стандартный способ обойти эту проблему?

Примечание. Одно из решений, о котором я мог подумать, - создать копию списка исключительно для целей итерации и удалить элементы из исходного списка в цикле. Я ищу лучший способ справиться с этим.

Ответы

Ответ 1

При использовании List<T> метод ToArray() помогает в этом сценарии значительно:

List<MyClass> items = new List<MyClass>();
foreach (MyClass item in items.ToArray())
{
    if (/* condition */) items.Remove(item);
}

Альтернативой является использование цикла for вместо foreach, но тогда вы должны уменьшать индексную переменную всякий раз, когда вы удаляете элемент i.e.

List<MyClass> items = new List<MyClass>();
for (int i = 0; i < items.Count; i++)
{
    if (/* condition */)
    {
        items.RemoveAt(i);
        i--;
    }
}

Ответ 2

Если ваш список является фактическим List<T>, вы можете использовать встроенный метод RemoveAll для удаления элементов на основе предиката

int numberOfItemsRemoved = yourList.RemoveAll(x => ShouldThisItemBeDeleted(x));

Ответ 3

Вы можете использовать индексирование целых чисел для удаления элементов:

List<int> xs = new List<int> { 1, 2, 3, 4 };
for (int i = 0; i < xs.Count; ++i)
{
    // Remove even numbers.
    if (xs[i] % 2 == 0)
    {
        xs.RemoveAt(i);
        --i;
    }
}

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

Ответ 4

Вы можете использовать LINQ для замены исходного списка новым списком путем фильтрации элементов:

IEnumerable<Foo> initialList = FetchList();
initialList = initialList.Where(x => SomeFilteringConditionOnElement(x));
// Now initialList will be filtered according to the condition
// The filtered elements will be subject to garbage collection

Таким образом, вам не нужно беспокоиться о циклах.

Ответ 5

Еще один трюк - это прокрутить список назад. Удаление элемента не повлияет на какой-либо элемент, с которым вы столкнетесь в остальной части цикла.

Я не рекомендую это или что-то еще. Все, что вам нужно для этого, возможно, можно сделать с помощью операторов LINQ, чтобы отфильтровать список по вашим требованиям.

Ответ 6

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

Ответ 7

Причина, по которой вы получаете сообщение об ошибке, заключается в том, что вы используете цикл foreach. Если вы думаете о том, как работает цикл foreach, это имеет смысл. Цикл foreach вызывает метод GetEnumerator в списке. Если вы хотите изменить количество элементов в Списке, то в Enumerator будет установлен правильный цикл foreach. Если вы удалите элемент, будет выбрана ошибка с ошибкой null, и если вы добавите элемент, цикл пропустит элемент.

Если вам нравятся выражения Linq и Lamda, я бы порекомендовал решение Дарина Димитрова, иначе я бы воспользовался решением Chris Schmich.

Ответ 8

Вы можете выполнить итерацию с помощью foreach следующим образом:

List<Customer> custList = Customer.Populate();

foreach (var cust in custList.ToList())

{

        custList.Remove(cust);

}

Примечание. Список ToList в списке переменных выполняет итерацию через список, созданный списком ToList, но удаляет элементы из исходного списка.

Надеюсь, что это поможет.