2013-01-07 4 views
20

遅延実行とデータの廃棄に関する質問があります。"using"ステートメント内でyieldを使用する場合、Disposeはいつ発生しますか?

は、次の例を考えてみましょう:

private IEnumerable<string> ParseFile(string fileName) 
{ 
    using(StreamReader sr = new StreamReader(fileName)) 
    { 
     string line; 
     while((line = sr.ReadLine()) != null) 
     { 
      yield return line; 
     } 
    } 
} 

private void LineReader(string fileName) 
{ 
    int counter = 0; 

    foreach(string line in ParseFile(fileName)) 
    { 
     if(counter == 2) 
     { 
      break; // will this cause a dispose on the StreamReader? 
     } else 
     { 
      Console.WriteLine(line); 
      counter++; 
     } 
    } 
} 

break文はすぐにParseFileで読者が処分するようになりますか、それはまだ文脈で考慮され、プログラム自体が閉じられるまで開いているファイルをロックしますか?

+2

クイックコンソールアプリを書いてみてください:) – jjxtra

+0

ああ、2番目のヒット時に 'break'を使うのではなく、' ParseFile'の最後に 'Take(2)'を追加するだけです。 – Servy

+1

イテレータブロックで何が起こっているのかを本当に分かりやすくするために、Jon Skeetの著書「C#in Depth」を読んでください。 –

答えて

16

ここでは、いくつか別々の問題が発生しています。

最初に、イテレータブロックのusingを処理します。 IEnumeratorIDisposableです。イテレータブロックを生成するコードは実際には、try/finallyブロック(が結果としてtry/finallyブロックを生成する)が結果としてfinallyブロックの内容が列挙子のDisposeメソッドで呼び出されるすでに呼び出されています。列挙子が処分されている限り、StreamReaderは漏れません。

そこで、列挙子が配置されているかどうかを尋ねます。 foreachステートメントはすべて列挙子のDisposeを呼び出します(IDisposableを実装する必要があります)。 breakまたはreturnステートメントを使用して終了した場合でも、正常終了した場合も同様です。

したがって、がないと、が漏れている(つまり、誰かがマシンを詰めていない)場合を除き、すべての状況下でリソースがリークすることはありません。

+0

これはもう少し複雑です。 OPのケースでは、イテレータはbreakステートメントのために絶対に処理します。しかし、ParseFileの呼び出しは、現在範囲外になっている 'local-using'の内部でStreamReaderへの参照を生成します。私はこれがファイナライザのキューにキューされ、収集されたときに廃棄されると思いますか? – n8wrl

+3

@ n8wrlいいえ、それは当てはまりません。 'IEnumerator'が' foreach'ループによって処理されると、 'StreamReader'(その時点で' using'の中にあったと仮定します)でdisposeを呼び出します。イテレータブロックが実装された方法です。これを可能にする。 – Servy

+0

私はPsychoDadのアドバイスを取って、それがうまくいくなら非常に涼しいので試してみてくださいね! – n8wrl

関連する問題