Почему IEnumerator<T> наследуется от IDisposable, а неуниверсальный IEnumerator — нет?

StackOverflow https://stackoverflow.com/questions/232558

  •  04-07-2019
  •  | 
  •  

Вопрос

Я заметил, что общий IEnumerator<T> наследует от IDisposable, а неуниверсальный интерфейс IEnumerator — нет.Почему он спроектирован таким образом?

Обычно мы используем оператор foreach для прохождения IEnumerator<T> пример.Сгенерированный код foreach на самом деле имеет блок try-finally, который вызывает Dispose() наконец.

Это было полезно?

Решение

В основном это был недосмотр. В C # 1.0 foreach never называется Dispose 1 . В C # 1.2 (представленном в VS2003 - странно нет версии 1.1) foreach начал проверять в блоке finally , реализовал ли итератор IDisposable - они должны были сделать это таким образом, потому что ретроспективное расширение IEnumerator на IDisposable нарушило бы реализацию IEnumerator . Если бы они решили, что для foreach в первую очередь полезно избавиться от итераторов, я уверен, что IEnumerator расширил бы IDisposable .

Когда вышли C # 2.0 и .NET 2.0, у них была новая возможность - новый интерфейс, новое наследование. Гораздо более разумно, чтобы интерфейс расширял IDisposable , чтобы вам не требовалась проверка времени выполнения в блоке finally, и теперь компилятор знает, что если итератор является IEnumerator < ; T > может отправлять безусловный вызов Dispose .

РЕДАКТИРОВАТЬ: невероятно полезно вызывать Dispose в конце итерации (как бы он ни заканчивался). Это означает, что итератор может удерживать ресурсы - что делает возможным, скажем, чтение файла построчно. Блоки итераторов генерируют реализации Dispose , которые гарантируют, что любые блоки finally , относящиеся к " текущей точке выполнения " итератора выполняются, когда он расположен - так что вы можете написать нормальный код внутри итератора, и очистка должна происходить соответствующим образом.

<Ч>

1 Возвращаясь к спецификации 1.0, она уже была указана. Я еще не смог проверить это более раннее утверждение, что реализация 1.0 не вызывала Dispose .

Другие советы

IEnumerable<T> не наследует IDisposable.Однако IEnumerator<T> наследует IDisposable, тогда как неуниверсальный IEnumerator — нет.Даже когда вы используете для каждого для необщего IEnumerable (который возвращает IEnumerator) компилятор все равно сгенерирует проверку IDisposable и вызовет Dispose(), если перечислитель реализует интерфейс.

Я предполагаю, что общий Enumerator<T> наследует от IDisposable, поэтому нет необходимости проводить проверку типа во время выполнения — он может просто пойти дальше и вызвать Dispose(), который должен иметь лучшую производительность, поскольку его, вероятно, можно оптимизировать, если перечислитель имеет пустой метод Dispose().

Я знаю, что это старая дискуссия, но я заново написал библиотеку, в которой я использовал IEnumerable из T / IEnumerator из T, где пользователи библиотеки могли бы реализовывать пользовательские итераторы, им просто нужно реализовывать IEnumerator из T.

Мне показалось очень странным, что IEnumerator из T наследовал бы от IDisposable. Мы реализуем IDisposable, если хотим освободить неуправляемые ресурсы, верно? Таким образом, это будет актуально только для перечислителей, которые на самом деле содержат неуправляемые ресурсы - например, поток ввода-вывода и т. Д. Почему бы просто не позволить пользователям реализовать IEnumerator из T и IDisposable на своем перечислителе, если это имеет смысл? В моей книге это нарушает принцип единственной ответственности - зачем смешивать счетную логику и уничтожать объекты.

IEnumerable` наследует IDisposing? Согласно отражателю .NET или MSDN . Вы уверены, что не путаете это с IEnumerator ? При этом используется IDisposing, потому что он предназначен только для перечисления коллекции и не предназначен для долговечности.

Немного трудно быть в этом определенным, если только вам не удастся получить ответ от самого AndersH или кого-то из его близких.

Однако я предполагаю, что это относится к " yield " Ключевое слово, которое было введено в C # одновременно. Если вы посмотрите на код, сгенерированный компилятором, когда "yield return x" используется, вы увидите метод, заключенный во вспомогательный класс, который реализует IEnumerator; IEnumerator наследуется от IDisposable, что гарантирует его очистку после завершения перечисления.

IIRC Все, что связано с IEnumerable < T > и IEnumerable , является результатом того, что IEnumerable предшествовал материалу шаблона .Net. Я подозреваю, что ваш вопрос такой же.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top