1. ホーム
  2. c#

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

2023-08-02 16:40:29

質問

私は、一般的な IEnumerator<T> は IDisposable を継承しますが、非ジェネリックなインターフェースである IEnumerator はそうではありません。なぜこのような設計になっているのでしょうか?

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

どのように解決するのか?

基本的にそれは見落としでした。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 を拡張する方がはるかに理にかなっており、最終ブロックでの実行時チェックが不要になります。また、コンパイラは、イテレータが IEnumerator<T> であれば、無条件に Dispose .

EDIT: これは信じられないほど便利です。 Dispose が反復処理の終了時に呼び出されるのは非常に便利です(どのように終了しようとも)。これは、イテレータがリソースを保持できることを意味し、これにより、たとえば、ファイルを 1 行ずつ読み取ることが可能になります。イテレータブロックは Dispose の実装を生成し、すべての finally ブロックは、イテレータが破棄されたときに実行されます。したがって、イテレータ内で通常のコードを書くことができ、クリーンアップは適切に行われます。


1 1.0 仕様を振り返ってみると、すでに指定されていました。1.0 の実装では Dispose .