Что происходит с перечислителями, если исходная коллекция модифицирована .NET - PullRequest
2 голосов
/ 30 мая 2011

У меня есть универсальный класс Tree, который будет реализовывать ICollection (и, следовательно, IEnumerable и IEnumerable).

Для этого мне нужно реализовать класс TreeEnumerator.

Каждый вызов Tree.GetEnumerator () возвращает новый экземпляр TreeEnumerator.

У меня есть 2 вопроса:

  1. Если вокруг много плавающих объектов TreeEnumerator, и базовое дерево изменяется, то что происходит? как справиться с этим?

  2. Лучше ли делать CopyToArray для всех элементов дерева (внутри TreeEnumerator для упрощения обхода массива) при создании TreeEnumerator или выполнять обход по одному шагу для каждого MoveNext?

Я знаю, что CopyToArray легко обходится один раз, но стоит места.

РЕДАКТИРОВАТЬ :

После ознакомления с механизмом версий:

Можете ли вы указать пример кода этого механизма управления версиями? Должны быть стандартные имена и способ доступа, потому что цикл foreach будет нуждаться в этой проверке для каждого MoveNext

Ответы [ 3 ]

1 голос
/ 30 мая 2011

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

Например, из документов для List<T>.GetEnumerator():

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

(На практике он будет выбрасывать InvalidOperationException.)

Обратите внимание, что одновременные коллекции в .NET 4 явно позволяют изменять коллекцию без аннулирования итератора.Обычно итератор будет видеть только исходные элементы, как если бы был сделан снимок, когда был вызван GetEnumerator().

1 голос
/ 30 мая 2011

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

Лично я бы избежал сценария;лучше спроектировать его так, чтобы вы перечислили либо , либо , но не оба одновременно.

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

1 голос
/ 30 мая 2011

Способ обработки этого в коллекциях в платформе, например, в классе List<T>, заключается в сохранении номера версии для состояния списка. Когда содержимое списка каким-либо образом изменяется, номер версии увеличивается.

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

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

...