Обычно не рекомендуется разрабатывать классы коллекций, которые позволяют вам изменять коллекцию при перечислении, если только вы не собираетесь разрабатывать что-то поточно-ориентированное, чтобы это было возможно (например, добавление из одного потока при перечислении из другого). * * 1001
Причины несметные. Вот один.
Ваш класс MyEnumerator
работает путем увеличения внутреннего счетчика. Его свойство Current
предоставляет значение по данному индексу в ArrayList
. Это означает, что перечисление в коллекции и удаление «каждого» элемента на самом деле не будут работать должным образом (т. Е. Не удалит каждый элемент в списке).
Рассмотрим эту возможность:
Код, который вы разместили, фактически сделает это:
- Вы начинаете с увеличения вашего индекса до 0, что дает вам
Current
"камня". Вы удаляете «камень».
- Теперь у коллекции есть
["roll", "rain dogs"]
, и вы увеличиваете свой индекс до 1, делая Current
равным "дождевым собакам" (НЕ "бросать") . Далее вы удаляете «дождевых псов».
- Теперь коллекция имеет
["roll"]
, и вы увеличиваете свой индекс до 2 (то есть> Count
); поэтому ваш счетчик думает, что он закончен.
Есть и другие причины, по которым это проблематичная реализация. Например, кто-то, использующий ваш код, может не понимать, как работает ваш перечислитель (ни не должен они - реализация не должна иметь значения), и поэтому не понимает, что стоимость вызова Remove
в пределах foreach
За каждую итерацию за блок взимается штраф IndexOf
, т. е. линейный поиск (см. документацию MSDN по ArrayList.Remove
, чтобы проверить это).
По сути, я имею в виду следующее: вы не хотите, чтобы могли удалять элементы из цикла foreach
(опять же, если вы не разрабатываете что-то поточно-ориентированное. .. возможно ).
ОК, так какова альтернатива? Вот несколько моментов, с которых можно начать:
- Не создавайте свою коллекцию так, чтобы она позволяла - не говоря уже о ожидать - изменений в перечислении. Это приводит к любопытному поведению, такому как пример, который я привел выше.
- Вместо этого, если вы хотите предоставить возможности массового удаления, рассмотрите такие методы, как
Clear
(для удаления всех элементов) или RemoveAll
(для удаления элементов, соответствующих указанному фильтру).
- Эти методы массового удаления могут быть реализованы довольно легко.
ArrayList
уже имеет метод Clear
, как и большинство классов коллекций, которые вы можете использовать в .NET. В противном случае, если ваша внутренняя коллекция проиндексирована, распространенным методом удаления нескольких элементов является перечисление из верхнего индекса с использованием цикла for
и вызов RemoveAt
для индексов, для которых требуется удаление (обратите внимание, что это устраняет сразу две проблемы: возвращаясь назад сверху, вы гарантируете доступ к каждому элементу в коллекции, более того, используя RemoveAt
вместо Remove
, вы избегаете штрафа за повторный линейный поиск).
- В качестве дополнительного примечания я настоятельно рекомендую для начала избегать неуниверсальных коллекций, таких как
ArrayList
. Вместо этого используйте жестко типизированные универсальные аналоги, такие как List(Of Album)
(при условии, что у вас был Album
класс - в противном случае List(Of String)
все еще более безопасен, чем ArrayList
).