web-dev-qa-db-ja.com

IEnumerator <T>がIDisposableから継承するのに、非ジェネリックIEnumeratorは継承しないのはなぜですか?

ジェネリックIEnumerator<T>はIDisposableから継承しますが、非ジェネリックインターフェイスIEnumeratorは継承しません。なぜこのように設計されているのですか?

通常、foreachステートメントを使用してIEnumerator<T>インスタンスを通過します。生成されたforeachのコードには、実際には、finallyでDispose()を呼び出すtry-finallyブロックがあります。

71
Morgan Cheng

基本的にそれは見落としでした。 C#1.0では、foreach決して呼び出されませんDispose 1。 C#1.2(VS2003で導入-1.1はありません、奇妙なことに)では、foreachはイテレータがfinallyを実装したかどうかをIDisposableブロックでチェックし始めました-彼らはそれをしなければなりませんでしたそうすれば、遡及的にIEnumeratorIDisposableに拡張すると、全員のIEnumeratorの実装が壊れてしまうからです。そもそもforeachがイテレータを破棄することが有用であることがわかったとしたら、IEnumeratorIDisposableを拡張したはずです。

ただし、C#2.0と.NET 2.0がリリースされたとき、新しいインターフェイス、新しい継承という新しい機会がありました。インターフェースをIDisposableに拡張して、finallyブロックで実行時チェックを行う必要がないようにする方がはるかに理にかなっています。コンパイラーは、イテレーターがIEnumerator<T>であるかどうかを認識します。 Disposeへの無条件の呼び出しを発行できます。

編集:反復の最後にDisposeが呼び出されると非常に便利です(ただし、終了します)。これは、イテレータがリソースを保持できることを意味します。これにより、たとえば、ファイルを1行ずつ読み取ることが可能になります。イテレータブロックはDispose実装を生成し、イテレータの「現在の実行ポイント」に関連するfinallyブロックが破棄されたときに実行されるようにします。これにより、イテレータ内で通常のコードを記述できます。クリーンアップは適切に行う必要があります。


1 1.0仕様を振り返ると、すでに指定されています。 1.0の実装がDisposeを呼び出さなかったという、この以前のステートメントをまだ確認できていません。

80
Jon Skeet

IEnumerable <T>はIDisposableを継承しません。ただし、IEnumerator <T>はIDisposableを継承しますが、非ジェネリックIEnumeratorは継承しません。非ジェネリックIEnumerable(IEnumeratorを返す)にforeachを使用する場合でも、コンパイラーはIDisposableのチェックを生成し、列挙子がインターフェイスを実装している場合はDispose()を呼び出します。

ジェネリックEnumerator <T>はIDisposableを継承しているので、実行時型チェックは必要ありません。Dispose()を呼び出すだけで、パフォーマンスが向上するはずです。列挙子には空のDispose()メソッドがあります。

5
Mark Cidade

私はこれが古い議論であることを知っていますが、ライブラリのユーザーがTのIEnumeratorを実装するだけでカスタムイテレータを実装できるT/IEnumeratorのIEnumerableを使用するライブラリを合理的に作成しました。

TのIEnumeratorがIDisposableから継承するのは非常に奇妙だと思いました。管理されていないリソースを解放したい場合は、IDisposableを実装しますか?したがって、IOストリームなどのように、実際にアンマネージリソースを保持する列挙子にのみ関連します。意味がある場合は、ユーザーにTのIEnumeratorとIDisposableの両方を列挙子に実装させないのはなぜですか?私の本では、これは単一責任の原則に違反しています-なぜ列挙子ロジックとオブジェクトの破棄を混在させるのですか?.

4
JAXN

IEnumerable`はIDisposedを継承しますか? .NETリフレクターまたは [〜#〜] msdn [〜#〜] によると。 IEnumerator と混同していませんか? IDisposedを使用するのは、コレクションを列挙するためだけであり、寿命を意図していないためです。

0
Aaron Powell

AndersH自身、または彼の近くの誰かから応答を得ることができなければ、これについて決定的になるのは少し難しいです。

ただし、C#で同時に導入された「yield」キーワードに関連していると思います。 「yieldreturnx」が使用されているときにコンパイラーによって生成されたコードを見ると、IEnumeratorを実装するヘルパークラスにラップされたメソッドがあります。 IEnumeratorをIDisposableの子孫にすることで、列挙が完了したときにIEnumeratorをクリーンアップできるようになります。

0
Bevan