BaseStream と Flush

id:parlin:20060103#1136299930より

ま、らっぱ〜被せたらずっとそれを使えって話なんでしょうねえ。

これって結構使いにくいんですよね。たとえば、

public interface Ixxx
{
  void Save(Stream stream)
}

Ixxx a = ...;
Ixxx b = ...;
Ixxx c = ...;

Stream s = ...;

a.Save(s);
b.Save(s);
c.Save(s);

みたいな場合に、a, b, c の各実装が全く異なる Writer を利用したがったらどうすればいいだろうか?*1

public class A : Ixxx
{
  public void Save(Stream stream)
  {
    using (BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8))
    {
      writer.Write(...);
       :
    }
  }
}

a がこのような実装だと、b.Save(s) の中で例外が発生するだろう。BinaryWriter が Dispose() されると、BinaryWriter は出力先である s を Close() するため、b.Save(s) が s に対して書き込みを実施できなくなってしまう。
これに対する対応は「 writer を Dispose() しないで Flush() を呼び出す」である。BinaryWriter は Dispose() または Close() を明示的に呼ばないで放置した場合、出力先ストリームからデタッチされたとみなされるようになっている。標準ライブラリの Writer/Reader 群ではみんなこうなっているんだが、個人的には Dispose() を明示的実装にして Close() を定義しているのと同様に、Detach() とか Release() みたいな名前で出力先を閉じない Close/Dispose を用意してほしかったと思う。

BaseStream

それで BaseStream プロパティなんだけど、内部でバッファリングを行っている Writer/Reader では、

private Stream baseStream;

public /*virtual*/ Stream BaseStream
{
  get
  {
    this.baseStream.Flush();
    return this.baseStream();
  }
}

みたいな実装になっていて、

StreamWriterとかでベースストリームの更新のタイミングが問題になる

ということは心配しなくてよい*2と思っていたんだけど、StreamWriter.BaseStream はそのような実装になっていないようだ。困るなあ。

ついでに

StreamWriter ではなくて StreamReader だけど、BaseStream とバッファリング関連で言うと DiscardBufferedData() は存在を知っておくべきメソッドですね。

*1:名前が a とか xxx とかなのは...

*2:そして BaseStream プロパティの参照はコストが高いことを考慮する必要がある