Pergunta

Eu tenho código que eu querer Para ficar assim:

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

Isso não funciona, porque você não pode remover da lista quando está dentro de um foreach Loop sobre essa lista:

Existe uma maneira comum de resolver o problema?

Eu posso mudar para um tipo diferente, se necessário.

Opção 2:

List<Type> Os;

...

while (Os.Count != 0)
     if (Os[0].cond)
         return;
     else
         Os.RemoveAt(0);

... // Other code

Feio, mas deve funcionar.

Foi útil?

Solução

Você realmente precisa fazer isso dentro de um foreach ciclo?

Isso alcançará os mesmos resultados que seus exemplos, ou seja, remover todos os itens da lista até o primeiro item que corresponde à condição (ou remova todos os itens se nenhum deles corresponder à condição).

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

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

Outras dicas

Você pode iterar através da lista para trás:

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

Em resposta ao seu comentário sobre querer sair quando você encontrar um item que não está removendo, apenas usar um loop enquanto seria a melhor solução.

Você nunca deve remover nada de uma coleção em que está iterando enquanto está dentro de um loop foreach. É basicamente como serrar o galho em que você está sentado.

Use seu enquanto alternativo. É o caminho a percorrer.

Eu sou um programador Java, mas algo assim funciona:

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

Acabei de ter esse problema com minha biblioteca de análises. Eu tentei o seguinte:

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

É bem simples, mas não pensei em nenhum ponto de ruptura.

Aqui está o Solução mais fácil com o mais simplório POR QUÊ

PROBLEMA:

Normalmente, estamos removendo da lista original, isso produz o problema de manter a contagem de listas e o local do iterador.

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

Solução - LINQ.ForEach:

Note que tudo que eu adicionei foi ToList(). Isso cria uma nova lista na qual você executa o FOREACH, portanto, você pode remover sua lista original, mas continua iterando por toda a lista.

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

Solução - regular foreach:

Esta técnica também funciona para regular foreach declarações.

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

Observe que esta solução não funcionará se sua lista original contiver struct elemento.

Eu sei que você pediu outra coisa, mas se você deseja remover condicionalmente um monte de elementos, pode usar a expressão lambda:

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

Eu tentaria encontrar o índice do primeiro item que não satisfazia o predicado e remove (0, Índice) nele. Se nada mais, deve haver menos chamadas.

Atualização: adicionado para completude

Como vários responderam, você não deve modificar uma coleção enquanto a itera com Getenumerator () (exemplo foreach). A estrutura impede que você faça isso lançando uma exceção. A colução genérica para isso é iterar "manualmente" com for (Veja outras respostas). Tenha cuidado com o seu índice para não pular itens ou reavaliar o mesmo duas vezes (usando i-- ou iterando para trás).

No entanto, para o seu caso específico, podemos otimizar as operações de remoção (s) ... resposta original abaixo.


Se o que você deseja é remover todos os itens até que se encontre uma determinada condição (é isso que seu código faz), você pode fazer isso:

bool exitCondition;

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

Ou se você quiser usar uma única operação de remoção:

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

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

Nota: já que estou assumindo que item.Condition é bool, Estou a usar item.State Para salvar a condição de saída.

ATUALIZAÇÃO: Os limites adicionados verificando e salvando condição de saída para ambos os exemplos

Você pode fazer isso com Linq

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

Se você sabe que sua lista não é muito grande, você pode usar

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

que criará uma duplicata temporária da lista. Sua chamada remove () não estará interferindo no iterador.

Olhe para Enumerable.SkipWhile()

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

Geralmente, não mutações de uma lista, facilita muito o vivo. :)

Há uma boa discussão sobre isso em Removendo itens em uma lista enquanto itera através dela .

Eles propõem:

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

    list.Remove(elementToRemove);
}

A solução de Anzurio é provavelmente a mais direta, mas aqui está outro limpo, se você não se importa de adicionar um monte de interfaces/classes à sua biblioteca de utilitários.

Você pode escrever assim

List<Type> Os;
...
var en = Os.GetRemovableEnumerator();
while (en.MoveNext())
{
    if (en.Current.Cond)
        en.Remove();
}

Coloque a seguinte infraestrutura, inspirada por Java's Iterator<T>.remove, em sua biblioteca de serviços públicos:

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--;
    }
}

I just had the same problem and solved it by using the following:

foreach (Type o in (new List(Os))) { if (something) Os.Remove(o); }

It iterates through a copy of the list and removes from the original list.

Add the item to remove in a list, and then remove these items by using 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));
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top