Перечисляя коллекцию, затем изменяя ее, что является прецедентом для создания исключения? - PullRequest
2 голосов
/ 04 января 2010

При перечислении коллекции .NET MSDN сообщает , что:

Перечислитель остается действительным, пока коллекция остается неизменной. Если в коллекцию вносятся изменения, такие как добавление, изменение или удаление элементов, перечислитель безвозвратно аннулируется и его поведение не определено.

Что именно означает «безвозвратно признанный недействительным»?

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

Итак, предположим, что с этим деревом я удаляю какой-то другой узел (предположительно, я не удаляю узел, в котором я сейчас нахожусь), должен ли я по-прежнему лишать законной силы счетчик? Обратите внимание, что я не говорю о многопоточной операции здесь, просто об одном потоке, выполняющем цикл, и изменяющем коллекцию внутри тела цикла.

Является ли этот "закон" действительно этим законом, что даже если счетчик может продолжить, он не должен?

Ответы [ 2 ]

8 голосов
/ 04 января 2010

Действительно ли этот "закон" - закон, который, даже если счетчик может продолжаться, не должен?

Лично я считаю, что использование вашего счетчика, даже если теоретически это может продолжаться, является хорошей практикой.

Часто люди непреднамеренно помещают код, который изменяет коллекцию, в цикл foreach. Без этого в конкретном экземпляре, который в данный момент тестирует разработчик, он может не работать, но другое условие выполнения может легко привести к отказу.

Всегда бросая, вы заставляете разработчика обращаться с вашим кодом так же, как с коллекциями и перечислениями фреймворка, что я считаю хорошей вещью, поскольку это снижает уровень неожиданности при работе с вашей библиотекой.

2 голосов
/ 04 января 2010

Внедрение стандартных счетчиков коллекций делает его законом. Когда они создаются, он копирует личное целое число «версия» из объекта коллекции. Изменение коллекции увеличивает эту версию. Методы итератора сравнивают версию и, когда есть несоответствие, выдают. Нельзя обойти это.

Однако существует один класс коллекции, который позволяет изменять коллекцию во время ее перечисления: Microsoft.VisualBasic.Collection. Это необходимо для обеспечения совместимости с классом VB6 Collection. Возможно, вы захотите взглянуть на это, чтобы увидеть, как это делается. IIRC сохраняет WeakReference на всех итераторах, а затем обновляет итераторы при изменении коллекции. Конечно, это не является доказательством того, что удаление элемента и добавление его обратно может перечислить один и тот же объект дважды. И это не дешево.

...