Проблемы с удалением элементов из списка при повторении в списке
У меня есть цикл, который выполняет итерацию через элементы в списке. Мне нужно удалить элементы из этого списка в цикле на основе определенных условий. Когда я пытаюсь сделать это на С#, я получаю исключение. по-видимому, не разрешено удалять элементы из списка, который выполняется итерацией. Проблема наблюдалась с петлей 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, но удаляет элементы из исходного списка.
Надеюсь, что это поможет.