Strange “coleção foi modificada depois que o enumerador foi instanciado” exceção ”

StackOverflow https://stackoverflow.com/questions/844850

  •  20-08-2019
  •  | 
  •  

Pergunta

Talvez alguém possa me apontar na direção correta, porque estou completamente perplexo com isso.

Eu tenho uma função que simplesmente imprime uma lista vinculada de aulas:

    LinkedList<Component> components = new LinkedList<Component>();
    ...
    private void PrintComponentList()
    {
        Console.WriteLine("---Component List: " + components.Count + " entries---");
        foreach (Component c in components)
        {
            Console.WriteLine(c);
        }
        Console.WriteLine("------");
    }

o Component objeto realmente tem um costume ToString() Ligue como tal:

    int Id;
    ...
    public override String ToString()
    {
        return GetType() + ": " + Id;
    }

Essa função normalmente funciona bem - no entanto, eu encontro a questão de que, quando se passa para cerca de 30 entradas na lista, o PrintcomplentList foreach A declaração volta com um InvalidOperationException: Collection was modified after the enumerator was instantiated.

Agora, como você pode ver, não estou modificando o código dentro do loop for e não criei explicitamente nenhum threads, embora este esteja dentro de um ambiente XNA (se importante). Deve -se notar que a impressão é frequente o suficiente para que a saída do console esteja desacelerando o programa como um todo.

Estou completamente perplexo, mais alguém por aí encontrou isso?

Foi útil?

Solução

Suspeito que o local para começar a procurar estará em qualquer lugar onde você manipule a lista - ou seja, inserir/remover/assinar itens. Minha suspeita é que haverá um retorno de chamada/manipulador em algum lugar que esteja sendo demitido de forma assíncrona (talvez como parte do XNA pintar loops etc.), e que está editando a lista - causando essencialmente esse problema como uma condição de corrida.

Para verificar se é esse o caso, coloque algumas saídas de depuração/rastreamento em torno dos lugares que manipulam a lista e veja se ela já (e, em particular, pouco antes da exceção) executa o código de manipulação ao mesmo tempo que sua saída de console:

private void SomeCallback()
{
   Console.WriteLine("---Adding foo"); // temp investigation code; remove
   components.AddLast(foo);
   Console.WriteLine("---Added foo"); // temp investigation code; remove
}

Infelizmente, essas coisas geralmente são uma dor de depuração, pois mudar o código para investigá -lo geralmente muda o problema (a Heisenbug).

Uma resposta seria sincronizar o acesso; ou seja tudo Os lugares que editam a lista, use um lock em torno da operação completa:

LinkedList<Component> components = new LinkedList<Component>();
readonly object syncLock = new object();
...
private void PrintComponentList()
{
    lock(syncLock)
    { // take lock before first use (.Count), covering the foreach
        Console.WriteLine("---Component List: " + components.Count
              + " entries---");
        foreach (Component c in components)
        {
           Console.WriteLine(c);
        }
        Console.WriteLine("------");
    } // release lock
}

E em seu retorno de chamada (ou o que seja)

private void SomeCallback()
{
   lock(syncLock)
   {
       components.AddLast(foo);
   }
}

Em particular, uma "operação completa" pode incluir:

  • Verifique a contagem e foreach/for
  • Verifique a existência e Insira/remova
  • etc.

(ou seja, não as operações individuais/discretas - mas unidades de trabalho)

Outras dicas

Ao invés de foreach, Eu uso while( collection.count >0) Em seguida, use collection[i].

Não sei se isso é relevante para o OP, mas tive o mesmo erro e encontrei esse tópico durante uma pesquisa no Google. Consegui resolvê -lo adicionando uma quebra depois de remover um elemento no loop.

foreach( Weapon activeWeapon in activeWeapons ){

            if (activeWeapon.position.Z < activeWeapon.range)
            {
                activeWeapons.Remove(activeWeapon);
                break; // Fixes error
            }
            else
            {
                activeWeapon.position += activeWeapon.velocity;
            }
        }
    }

Se você deixar de fora o intervalo, receberá o erro "InvalidoperationException: a coleção foi modificada depois que o enumerador foi instanciado".

Usando Break Pode ser uma maneira, mas isso pode afetar sua série de operação. O que eu faço nesse caso em simplesmente converter o foreach ao tradicional for ciclo

for(i=0; i < List.count; i++)
{
    List.Remove();
    i--;
}

Isso funciona sem problemas.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top