2010-12-05 19 views
9

は、次のコードを考えてみましょう。一方でライターが破棄されたときにストリームが破棄されるのはなぜですか?

using (var ms = new MemoryStream()) 
{ 
    using(var writer = BinaryWriter(ms)) 
    { 
     writer.Write(/*something*/); 
     writer.Flush(); 
    } 

    Assert.That(ms.Length > 0); // Throws ObjectDisposedException 
} 

を、使い捨てのオブジェクトは、それのリソースを処分しなければなりません。私はそれを得るが、一方で、オブジェクトは作成せず、このリソースを所有していない、それは提供された - >呼び出しコードはそれに責任を負うべきである...いいえ?

私はこのような任意の他の状況を考えることはできませんが、それは自身の処分でそれらを処分するために使い捨てのオブジェクトを受け取る任意のクラスのためのフレームワークで一貫したパターンですか?

答えて

13

はあなただけのストリームごとにライターを持っています暗黙の前提があり、その作家は便宜上、ストリームの所有権を前提として - あなたがしてoblyクリーンアップする一つのことを持っています。

しかし、私は同意します。これは常に真実ではなく、しばしば不便です。いくつかの実装(DeflateStream、GZipStreamなど)では、選択することができます。さもなければ、唯一の実際のオプションは、ライターとその下層のストリームとの間にダミーストリームを注入することです。行われたのは正しいことをのコンストラクタパラメータを持つようにされているでしょう

using (var ms = new MemoryStream()) 
{ 
    using(var noClose = new NonClosingStreamWrapper(ms)) 
    using(var writer = BinaryWriter(noClose)) 
    { 
        writer.Write(/*something*/); 
        writer.Flush(); 
    } 

    Assert.That(ms.Length > 0); 
} 
4

私は完全にあなたに同意。これは一貫した動作ではありませんが、実装されている方法です。非常に直感的ではないこの動作に関するドキュメントの末尾にcommentsがあります。すべてのストリームライターは、基本ストリームの所有権を取り、それを処分するだけです。個人的に私はいつもの巣、このように私のusing声明:

using (var ms = new MemoryStream()) 
using(var writer = BinaryWriter(ms)) 
{ 
    writer.Write(/*something*/); 
} 

をあなたがアサートに入れて1のようなコードを書くことがないように。

0

http://www.yoda.arachsys.com/csharp/miscutil/

使用法のようなものになるだろう:IIRC NonClosingStreamWrapperは、まさにこれがないジョンスキートの「MiscUtil」ライブラリにありストリームライターは、コンストラクターが存在するときにストリームを配置する必要があるかどうかを示します。 Microsoftはそれをしなかったことを考えると、ストリームをラップしたが包まれたストリームへのDisposeの呼び出しを渡しません(ストリームとしてTの)NonDisposingStreamクラスを定義するためには良いかもしれません。次に、新しいNonDisposingStreamをStreamWriterのコンストラクタに渡すことができます。ストリームの廃棄は安全です(ストリームを自分自身で廃棄する必要があります)。

は、渡されたオブジェクトを処分することができるオブジェクトを持つことは有用です。このような振る舞いは、オブジェクトの作成者がその処分を処理する通常のパターンと一致しませんが、オブジェクトの作成者がオブジェクトが実際どれくらい長く必要になるか分からない状況がよくあります。たとえば、あるメソッドが新しいStreamを使用する新しいStreamWriterを作成することが期待される場合があります。 StreamWriterの所有者は、いつ処理する必要があるかを認識しますが、内部ストリームの存在を知ることはできません。内側のストリームの作成者は、外側のStreamWriterがどれくらい長く使われるか分かりません。 StreamWriterに渡されたストリームの所有権を持つことは、その特定の(共通の)ケースにおける廃棄問題を解決します。

0

私は、このラッパークラスを提案する:

public class BetterStreamWriter : StreamWriter 
{ 
    private readonly bool _itShouldDisposeStream; 

    public BetterStreamWriter(string filepath) 
     :base(filepath) 
    { 
     _itShouldDisposeStream = true; 
    } 

    public BetterStreamWriter(Stream stream) 
     : base(stream) 
    { 
     _itShouldDisposeStream = false; 
    } 

    protected override void Dispose(bool disposing) 
    { 
     base.Dispose(disposing && _itShouldDisposeStream); 
    } 
} 

オブジェクトは、彼らがインスタンス化していないものを処分するべきではありません。それがファイルストリームのライターなら、それは処分する必要があります。それが外部ストリームの場合、それはしないでください。

最初にファイルのパスを開く必要はありません。オブジェクトがファイルの書き込みと存続期間を管理するため、これは単一責任の原則に違反します。

+1

オブジェクトは、*所有していないものを廃棄してはなりません。オブジェクトを直接インスタンス化する以外の方法でオブジェクトの所有権を取得することができます(最も一般的には、ファクトリメソッドを呼び出すことによって)。ファクトリメソッドは通常、単一のオブジェクトだけを返すので、メソッドによって取得されたすべてのリソースがそのオブジェクトによって所有される必要があります。 'StreamWriter'が渡されたストリームの所有権を持つようにすると、ファクトリメソッドがストリームを合法的に構築し、それをカプセル化する' StreamWriter'を返すことが可能になります。 – supercat

+1

@supercat eh no。私はあなたに同意します。「オブジェクトは所有していないものを処分すべきではありません。それはそれを言う正しい方法です。しかし、この場合ではありません。 StreamWriterクラス自体を設計するには、呼び出し元がストリームをどのように扱っているのか、どちらにも当てはまりません。ストリームが複数のリーダーで使用される場合を考慮してください。次に、それを破棄したいかどうかに応じて、文を使って条件付きでStreamReaderをラップする必要がありますか? IDisposableは、ステートメントを使用しています。質問はありません。 –

+0

適切なアプローチは、 'StreamReader' /' StreamWriter'のコンストラクタが、呼び出し側が所有権が転送されるかどうかを(それ以降のバージョンの.NETで)行うことができるようにすることです。さもなければ、 'StreamReader'から取り出されたオーディオデータを非同期的に再生する方法を書く方法を考えてみましょう。オーディオを再生しているコードは、基礎となるストリームについては何も知らないかもしれません。また、 'StreamReader'を構築するコードは、再生コードがいつ実行されるか分かりません。ストリームが純粋にオーディオ再生の目的のために開かれた一般的なケースでは... – supercat

関連する問題