Странная «коллекция была изменена после того, как перечисление было создано» исключением

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

  •  20-08-2019
  •  | 
  •  

Вопрос

Возможно, кто -то сможет указать мне в правильном направлении, потому что я полностью озадачен этим.

У меня есть функция, которая просто распечатает LinkedList of Class:

    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("------");
    }

А Component У объекта на самом деле есть пользовательский ToString() позвонить как таковой:

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

Эта функция обычно работает нормально - однако я столкнулся с проблемой, которая приводит к примерно 30 записям в списке, PrintcomplentList foreach Заявление возвращается с InvalidOperationException: Collection was modified after the enumerator was instantiated.

Теперь, как вы можете видеть, я не изменяю код в цикле для цикла, и я явно не создавал никаких потоков, хотя это находится в среде XNA (если это имеет значение). Следует отметить, что распечатка достаточно частая, чтобы выход консоли замедляет программу в целом.

Я полностью озадачен, кто -нибудь еще столкнулся с этим?

Это было полезно?

Решение

Я подозреваю, что место, где можно начать искать, будет в любых местах, где вы манипулируете списком - т.е. вставьте/удаляет/повторно уцените элементы. Я подозреваю, что где-нибудь будет обратный обратный вызов/ровный, который будет уволен асинхронно (возможно, как часть XNA краска и т. Д.)

Чтобы проверить, есть ли это так, поместите некоторые выводы отладки/трассировки вокруг мест, которые манипулируют списком, и посмотрите, использует ли он когда -нибудь (и, в частности, непосредственно перед исключением) запускает код манипуляции одновременно с выводом вашей консоли:

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

К сожалению, такие вещи часто бывают болью для отладки, так как изменение кода, чтобы исследовать его, часто меняет проблему ( Хейзенбуг).

Одним из ответов было бы синхронизировать доступ; т.е. в все места, которые редактируют список, используют lock Вокруг всей операции:

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
}

И в вашем обратном вызове (или что -то еще)

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

В частности, «полная операция» может включать в себя:

  • Проверьте счет а также foreach/for
  • Проверьте наличие существования а также Вставить/удалить
  • так далее

(т. Е. Не индивидуальные/дискретные операции - но единицы работы)

Другие советы

Вместо foreach, Я использую while( collection.count >0) затем используйте collection[i].

Я не знаю, имеет ли это отношение к OP, но у меня была та же ошибка, и я нашел этот поток во время поиска Google. Я смог решить его, добавив перерыв после удаления элемента в цикле.

foreach( Weapon activeWeapon in activeWeapons ){

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

Если вы оставите перерыв, вы получите ошибку «Invalidoperation Exception: сбор был изменен после того, как перечисление было создано».

С использованием Break Может быть способ, но это может повлиять на вашу серию операций. Что я делаю в этом случае в простое преобразование foreach традиционным for петля

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

Это работает без каких -либо проблем.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top