Странная «коллекция была изменена после того, как перечисление было создано» исключением
-
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--;
}
Это работает без каких -либо проблем.