Ответ 1
Там полезный метод List<T>.RemoveAll(Predicate<T> match)
, который, как мне кажется, предназначен для этого: http://msdn.microsoft.com/en-us/library/wdka673a.aspx
Изменение коллекции при итерации через нее
У кого-нибудь хороший шаблон, позволяющий мне обойти невозможность удалить объекты, когда я прокручиваю перечислимую коллекцию (например, IList или KeyValuePairs в словаре)
Например, следующее не выполняется, поскольку оно изменяет список, который перечислит во время foreach
foreach (MyObject myObject in MyListOfMyObjects)
{
if (condition) MyListOfMyObjects.Remove(myObject);
}
В прошлом я использовал два метода.
Я заменил foreach на обратный цикл for (чтобы не изменять любые индексы, которые я перебираю, если я удаляю объект).
Я также попытался сохранить новую коллекцию объектов для удаления внутри цикла, затем прокрутил эту коллекцию и удалил объекты из исходной коллекции.
Они работают отлично, но не чувствуют себя хорошо, и мне было интересно, если кто-нибудь придумает более элегантное решение проблемы
Там полезный метод List<T>.RemoveAll(Predicate<T> match)
, который, как мне кажется, предназначен для этого: http://msdn.microsoft.com/en-us/library/wdka673a.aspx
Это вроде простодушный, но когда я планирую удалить элементы из IEnumerable/IList, я обычно делаю копию:
foreach (MyObject myObject in new List<MyObject>(MyListOfMyObjects))
{
if (condition) MyListOfMyObjects.Remove(myObject);
}
Это не самый эффективный способ сделать это, но его легко читать. Преждевременная оптимизация и все такое.
Сделайте инверсию, создав новый список:
List myFilteredList = new List();
foreach (MyObject myObject in myListOfMyObjects)
{
if (!condition) myFilteredList.Add(myObject);
}
Затем используйте новый список, где вам это нужно.
Вы также можете легко использовать выражение LINQ, снова инвертируя условие. Это имеет дополнительное преимущество, не создавая новую структуру, но также и ловушки ее ленивого перечисления:
var myFilteredList = from myObject in myListOfMyObjects
where !condition
select myObject;
Однако, если вам действительно нужно удалить элементы из списка, я обычно использую подход "создать новый список, а затем повторить и удалить".
Я просто наткнулся на этот пост и думал, что поделюсь.
void RemoveAll(object condition)
{
bool found = false;
foreach(object thisObject in objects)
{
if (condition)
{
objects.Remove(thisObject);
found = true;
break; //exit loop
}
}
// Call again recursively
if (found) RemoveAll(condition);
}
Мне не нравится обратная идея цикла, так как это работает только на определенных структурах данных.
В общем, я бы использовал второй метод и накапливал элементы, которые нужно удалить в отдельной коллекции "для удаления". Если удаление может привести к тому, что существующие итерации будут недействительными (как это происходит с любой сбалансированной коллекцией деревьев, например), я не вижу способа обойти это.
Единственный другой метод, который я иногда использовал, - перезапустить всю итерацию, когда вы найдете первый элемент для удаления. Если вы проделаете это без поиска каких-либо предметов, которые будут удалены, функция будет закончена. Это неэффективно, но иногда необходимо, если удаление одного элемента из коллекции может изменить набор элементов, которые необходимо удалить.
Я понимаю, что теперь это может быть мертво, но я всегда это делаю:
foreach (MyObject myObject в MyListOfMyObjects)
{
if (condition) MyListOfMyObjects.Remove(myObject);
break;
}
Объект удаляется, а затем цикл выходит, альта!
У меня есть словарь и вы хотите удалить все значения. Когда каждое значение расположено, он удаляется из словаря, который создает проблему, которую вы обсуждаете. Я сделал следующее:
foreach (var o in dictionary.Values.ToList())
{
o.Dispose();
}