Почему IEnumerator наследуется от IDisposable, а не универсальный IEnumerator - нет?
Я заметил, что общий IEnumerator<T>
наследуется от IDisposable, но не универсальный интерфейс IEnumerator этого не делает. Почему он разработан таким образом?
обычно мы используем оператор foreach, чтобы пройти через IEnumerator<T>
экземпляра. Сгенерированный код в цикле foreach на самом деле и попробовать-наконец-то блок, который вызывает метод Dispose() в конце концов.
6 ответов:
в основном это была оплошность. В C# 1.0,
foreach
никогда под названием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
.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, он может очищаться, когда перечисление завершено.