Из того, что я видел на http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx, и статьи Джона Скита, сама спецификация С# говорит об этом. В чем причина?
Почему метод Reset() в классе Enumerator должен вызывать NotSupportedException()?
Ответ 1
Это не то, как я прочитал С# spec [Word doc]. Раздел 10.14.4 "Объекты перечислителя":
... [E] объекты числителя не поддерживают метод IEnumerator.Reset. Вызов этого метода вызывает исключение System.NotSupportedException.
Однако этот раздел (и оператор) специфичен для "объектов перечислителя", который определяется как:
Когда член функции, возвращающий тип интерфейса перечислителя, реализуется с использованием блока итератора, вызов элемента функции не сразу выполняет код в блоке итератора. Вместо этого создается и возвращается объект перечисления .
Другими словами, "объект перечислителя" является компилятором, сгенерированным IEnumerator
1. Нет ограничений для каждого IEnumerator
, только те, которые генерируются из блоков итератора (aka yield
).
Почему? Я подозреваю, потому что это невозможно сделать в общем случае - без сохранения каждой ценности и связанных с этим ограничений памяти. Объедините это с тем, что IEnumerator.Reset()
редко используется (когда последний раз, когда вы Reset перечислитель?) И что MSDN специально призывает, что ему нужно не будут реализованы:
Метод Reset предоставляется для совместимости COM. Это необязательно должно быть реализовано; вместо этого разработчик может просто выбросить исключение NotSupportedException.
и вы можете вырезать много сложностей, и никто не замечает этого.
Как и требовать, чтобы он выбрал 2 я предполагаю, что это просто проще, чем позволить разработчику решить. IMO, это немного требует запроса - могут быть разумные случаи, когда компилятор (или другая реализация 1) может генерировать метод Reset, но я не вижу его как реальная проблема.
1 Технически спецификация оставляет возможность других реализаций:
Объект перечислителя обычно является экземпляром класса перечислителя, сгенерированного компилятором, который инкапсулирует код в блок итератора и реализует интерфейсы перечислителя, но возможны другие методы реализации.
но я не знаю других конкретных реализаций. Независимо от того, чтобы быть совместимым, другим реализациям "объекта-перечислителя" пришлось бы также метать NotSupportedException
.
2 Nitpicker corner: Я думаю, что может быть какое-то придирчиво даже в "требовании" бросить. Спектр, не используя предпочтительный "ДОЛЖЕН, ДОЛЖЕН, МОЖЕТ" формулировать, оставляет его немного открытым. Я читаю "причины" больше как примечание о реализации - не требование. Опять же, я не читал всю спецификацию, поэтому, возможно, они определяют эти термины немного больше или более явны в другом месте.
Ответ 2
Невозможно правильно поддерживать во всех последовательностях; многие только один раз (сетевые потоки и т.д.). И если вы не можете полагаться на это все на время, это бесполезно, поскольку абстракция нарушена. Конечно, у вас может быть IResettableEnumerator
, но Reset()
on IEnumerator
не работает
Работа. Честно говоря, это была ошибка (ИМО).
Я подозреваю, что это сделало бы итераторы более сложными даже более сложными (они в настоящее время являются одной из двух наиболее сложных частей компилятора, я не помню, что такое "верх", или анонимные методы/захваченные переменные).
Ответ 3
Вот что говорит MSDN
Метод Reset предоставляется для COM совместимость. Он не обязательно требуется; вместо этого исполнитель может просто бросить исключение NotSupportedException.
http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.reset.aspx > MSDN IEnumerator..::. Метод сброса
Он не говорит, что должен, он просто говорит, что может.
EDIT: Однако, как отметил Марк, в С# 2.0 Spec есть разница
22.2 Объекты перечислителя
Обратите внимание, что объекты перечислителя не поддерживают метод IEnumerator.Reset. Вызов этого метода вызываетИсправление System.NotSupportedException выброшены.