C# StreamReder/WriterのleaveOpen

ファイル関係のプログラムを書いていて、ファイルをクローズしたくないというシチュエーションがあったので、調べてみた。

初めに

ファイル(Stream)関連を使う場合、using句を使い、処理区間を明確にし、処理終了後にDisposeが呼び出されるようにするのが一般的。

ただ、クラスやメソッド間をStreamでやり取りし、呼び出された側で、それをStreamWriterやStreamReaderなどで利用したい場合、通常では問題があった。

問題点

例えば以下のコード。

class Program {
	static void Main(string[] args) {
		string filename = @"hoge.txt";
		using (var stream = File.Open(filename, FileMode.CreateNew)) {
			using (var writer = new StreamWriter(stream)) {
				writer.WriteLine("hogehoge");
			}
			Console.WriteLine(stream.Length);
		}
	}
}

5~7行目で、その前にオープンしていたStreamをStreamWriter形式で、開きなおした。

ただ、この例では、7行目でwriterがクローズされると同時に、streamもクローズされてしまった。

そのため、8行目のConsole.WriteLine(stream.Length);で既に閉じていると、例外が発生した。

たぶん、上記の様に、すでにオープンしているものを、再オープンするような実装は、どのオブジェクトで、最終的にリソースの管理がされているのかわからなくなるので、望ましくないのだと思う。

ただ、今回必要となったため、writer側でクローズされないようにするには、どうすればいいのか調査してみた。

調査結果

色々、検索してみたところ、StreamReader/WriterのコンストラクタにleaveOpenという引数があることが分かった。

これは、Disposeでクローズしないというフラグらしい。

以下のようなコードで試してみた。

class Program {
	static void Main(string[] args) {
		string filename = @"hoge.txt";
		using (var stream = File.Open(filename, FileMode.CreateNew)) {
			using (var writer = new StreamWriter(stream, Encoding.ASCII, 4096, true)) {
				writer.WriteLine("hogehoge");
				writer.Flush();
			}
			try {
				File.Delete(filename);
			}
			catch (IOException ex) {
				Console.WriteLine(ex.ToString());
			}
			Console.WriteLine(stream.Length);
		}
		try {
			File.Delete(filename);
		}
		catch (IOException ex) {
			Console.WriteLine(ex.ToString());
		}
	}
}

ファイルがオープンされている状況が継続しているのであれば、10行目のファイル削除ができないので、例外が発生するはず。

実際実施した場合、例外が発生した。また、その後のstream.Lengthでも正しく値が取り出せた。

大外のstreamの終了で、ファイルがクローズされているかどうかも、18行目のファイル削除で例外が発生せず、また、ファイルも削除されていたことから問題ないと認識できた。

注意点

  • leaveOpenを使った場合、Close処理が行われていなかったため、using句から抜ける際に、writer側のバッファがフラッシュされていなかった。using句から抜ける前に、Fluhs()を個別に入れてあげる必要がある。
  • コンストラクタでleaveOpenを指定する場合、エンコーディング、バッファサイズの指定が必要になる。
    エンコーディングは、適時設定すればいいのだが、バッファサイズにはどれくらい必要なのだろうか。
    調べたところ、現在のデフォルトは1024で、最大は4096だそうだ。

その他

MSDNのStreamWriter(Stream, Encoding, Int32, Boolean)の日本語説明に「円コーディング」と入っていたのがちょっと笑った。

他は、「エンコーディング」となっているのに、ここだけなぜ違う?

コメント

タイトルとURLをコピーしました