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


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

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

6 65

6 ответов:

в основном это была оплошность. В C# 1.0, foreachникогда под названием Dispose1. С 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.

EDIT: это невероятно полезно для Dispose вызывается в конце итерации (однако она заканчивается). Это означает, что итератор может удерживать ресурсы - что делает возможным для него, скажем, читать файл строка за строкой. Итератор блокирует генератор Dispose реализации, которые гарантируют, что любой finally блоки, относящиеся к" текущей точке выполнения " итератора, выполняются при его удалении - поэтому вы можете написать нормальный код в итераторе, и очистка должна происходить соответствующим образом.


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

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

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

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

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

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

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

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

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