Tenho o caso clássico de tentar remover um item de uma coleção enquanto o enumera em um loop:
List<int> myIntCollection = new List<int>();
myIntCollection.Add(42);
myIntCollection.Add(12);
myIntCollection.Add(96);
myIntCollection.Add(25);
foreach (int i in myIntCollection)
{
if (i == 42)
myIntCollection.Remove(96); // The error is here.
if (i == 25)
myIntCollection.Remove(42); // The error is here.
}
No início da iteração, após uma mudança ocorrer, um InvalidOperationException
é lançado, porque os enumeradores não gostam quando a coleção subjacente muda.
Preciso fazer alterações na coleção durante a iteração. Existem muitos padrões que podem ser usados para evitar isso , mas nenhum deles parece ter uma boa solução:
Não exclua dentro deste loop; em vez disso, mantenha uma “Lista de Exclusão” separada, que você processa após o loop principal.
Normalmente, essa é uma boa solução, mas no meu caso, preciso que o item desapareça instantaneamente, pois "esperar" até que o loop principal realmente exclua o item altera o fluxo lógico do meu código.
Em vez de excluir o item, basta definir um sinalizador no item e marcá-lo como inativo. Em seguida, adicione a funcionalidade do padrão 1 para limpar a lista.
Isso iria trabalhar para todas as minhas necessidades, mas isso significa que um monte de código terá que mudar a fim de verificar a bandeira inativo sempre que um item é acessado. Isso é administração demais para o meu gosto.
De alguma forma, incorpore as idéias do padrão 2 em uma classe que deriva de
List<T>
. Esta Superlista tratará do sinalizador inativo, da exclusão de objetos após o fato e também não exporá os itens marcados como inativos aos consumidores de enumeração. Basicamente, ele apenas encapsula todas as idéias do padrão 2 (e subsequentemente do padrão 1).Existe uma classe como esta? Alguém tem código para isso? Ou há um modo melhor?
Disseram-me que acessar em
myIntCollection.ToArray()
vez demyIntCollection
resolverá o problema e me permitirá deletar dentro do loop.Isso parece um padrão de design ruim para mim, ou talvez seja bom?
Detalhes:
A lista conterá muitos itens e irei remover apenas alguns deles.
Dentro do loop, estarei realizando todos os tipos de processos, incluindo, removendo etc., portanto, a solução precisa ser bastante genérica.
O item que preciso excluir pode não ser o item atual no loop. Por exemplo, posso estar no item 10 de um loop de 30 itens e precisar remover o item 6 ou o item 26. Andar para trás pela matriz não funcionará mais por causa disso. ; o (
fonte
Respostas:
A melhor solução geralmente é usar o
RemoveAll()
método:Ou, se você precisar remover certos elementos:
Isso pressupõe que seu loop se destina exclusivamente a fins de remoção, é claro. Se você fazer necessidade de processamento adicional, então o melhor método é geralmente a utilização de um
for
ouwhile
loop, desde então você não está usando um enumerador:Retroceder garante que você não pule nenhum elemento.
Resposta para editar :
Se você deseja remover elementos aparentemente arbitrários, o método mais fácil pode ser apenas manter o controle dos elementos que deseja remover e removê-los todos de uma vez depois. Algo assim:
fonte
Se você deve enumerar a
List<T>
e remover dele, então sugiro simplesmente usar umwhile
loop em vez de umforeach
fonte
Sei que este post é antigo, mas pensei em compartilhar o que funcionou para mim.
Crie uma cópia da lista para enumerar e, em seguida, no for each loop, você pode processar os valores copiados e remover / adicionar / qualquer coisa com a lista de origem.
fonte
Quando você precisa iterar por uma lista e pode modificá-la durante o loop, é melhor usar um loop for:
Claro que você deve ter cuidado, por exemplo, eu decremento
i
sempre que um item é removido, caso contrário, iremos pular as entradas (uma alternativa é voltar para trás na lista).Se você tiver o Linq, você deve apenas usar
RemoveAll
como dlev sugeriu.fonte
--i
.Conforme você enumera a lista, adicione aquele que deseja MANTER a uma nova lista. Depois, atribua a nova lista ao
myIntCollection
fonte
Vamos adicionar seu código:
Se você quiser mudar a lista enquanto estiver em um foreach, você deve digitar
.ToList()
fonte
E se
fonte
foreach (int i in myIntCollection.ToArray()) { myIntCollection.Remove(42); }
para qualquer enumerável e,List<T>
especificamente, oferece suporte a esse método mesmo no .NET 2.0.Se você estiver interessado em alto desempenho, pode usar duas listas. O seguinte minimiza a coleta de lixo, maximiza a localidade da memória e nunca remove realmente um item de uma lista, o que é muito ineficiente se não for o último item.
fonte
Para aqueles que podem ajudar, escrevi este método de extensão para remover itens correspondentes ao predicado e retornar a lista de itens removidos.
fonte