ストリームリーダーを破棄するとストリームが閉じますか?


166

書き込みを行うメソッドにストリームを送信しています。これらのメソッドでは、バイナリリーダー/バイナリを使用しています。リーダ/ライタが破棄されたとき、usingまたは参照されていないときに、ストリームも閉じられますか?

BinaryReader / Writerを送信しますが、StreamReaderも使用しています(おそらく回避する必要があります。GetLineとReadLineにのみ使用しています)。ライター/リーダーが閉じられるたびにストリームを閉じると、これは非常に面倒です。

回答:


204

はい、StreamReaderStreamWriterBinaryReaderBinaryWriterお電話の際は、すべての近くに/その基礎となるストリームを処分しDispose、それらに。彼らはしていないリーダ/ライタはかかわら収集ただのゴミである場合は、ストリームを処分-あなたはいつもできれで、リーダ/ライタを処分すべきであるusing声明。(実際、これらのクラスにはファイナライザがありません。

個人的には、ストリームのusingステートメントも使用することを好みます。using中括弧なしでステートメントを入れ子にすることができます。

using (Stream stream = ...)
using (StreamReader reader = new StreamReader(stream, Encoding.Whatever))
{
}

usingストリームのステートメントは多少冗長ですが(StreamReaderコンストラクターが例外をスローしない限り)、私はそれを排除StreamReaderし、後でストリームを直接使用する場合は、すでに適切な処分をしているので、ベストプラクティスを検討しますセマンティクス。


2
ああ、それはDisposeを呼び出すときにのみ発生し、おそらくファイナライズ中ではありません。
Nefzen、2009年

1
@Nefzen:オブジェクトがファイナライズされる順序が保証されていないためです。StreamReaderと基になるStreamの両方がファイナライズに適格である場合、GCは最初にストリームをファイナライズする可能性があります-次にstreamreaderはストリームへの参照を持ちません。このため、ファイナライズ内ではアンマネージリソースのみを解放できます(たとえば、FileStreamはファイナライズでWindowsファイルハンドルを閉じます)。もちろん、処分しない場合でも、ストリームは引き続き最終的に収集されます(ファイルは閉じられます)。ストリームを破棄しないのは非常に悪い習慣です。
JMarsch 2009年

13
このネストにより、VSコードアナライザーは文句を言います:CA2202 : Microsoft.Usage : Object 'stream' can be disposed more than once in method '...'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.それは無視されるべきですか?今のところ例外はありませんでした...
HB

15
@HB:この場合は無視しても安全です。または、へのコンストラクター呼び出しでストリームを作成することもできますStreamReader。警告ルックスはのためのドキュメントことを考えると、私におかしなIDisposable.Dispose明示的状態:「オブジェクトのDisposeメソッドは、オブジェクトが最初のものの後にすべての呼び出しを無視しなければならない、複数回呼び出された場合、そのDisposeメソッドがある場合、オブジェクトが例外をスローしてはいけません。複数回呼び出されました。」
ジョンスキート

5
@JonSkeet:実際にこれに関するページがあります。正解でした。これは偽です:)
HB

45

これは古いものですが、今日は同じようなことをしたいと思い、状況が変わったことがわかりました。.net 4.5以来、leaveOpen議論があります:

public StreamReader( Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen )

唯一の問題は、他のパラメーターに何を設定するかが完全に明らかではないことです。ここにいくつかの助けがあります:

StreamReaderコンストラクター(ストリーム)のmsdnページから:

このコンストラクターは、エンコーディングをUTF8Encoding、ストリームパラメーターを使用するBaseStreamプロパティ、および内部バッファーサイズを1024バイトに初期化します。

それだけで、ソースコードdetectEncodingFromByteOrderMarksによる判断はtrue

public StreamReader(Stream stream)
        : this(stream, true) {
}

public StreamReader(Stream stream, bool detectEncodingFromByteOrderMarks)
        : this(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize) {
}

これらのデフォルトの一部が公開されている場合、または引数がオプションであり、必要なものだけを指定できるようになっていると便利です。


とてもいい情報!この新しいパラメータについて聞いたことがないので、実際には非常に理にかなっています。
julealgon 14年

3
:私のような怠け者のために、ストリームを開いたままにする短い答えは次のようになりますusing (var streamReader = new StreamReader(myStream, Encoding.UTF8, true, 1024, true))
beawolf

29

はい、そうです。これは、Reflectorを使用して実装を確認することで確認できます。

protected override void Dispose(bool disposing)
{
    try
    {
        if ((this.Closable && disposing) && (this.stream != null))
        {
            this.stream.Close();
        }
    }
    finally
    {
        if (this.Closable && (this.stream != null))
        {    
            this.stream = null;    
            this.encoding = null;
            this.decoder = null;
            this.byteBuffer = null;
            this.charBuffer = null;
            this.charPos = 0;
            this.charLen = 0;
            base.Dispose(disposing);
        }
    }
}

13

6年遅れますが、多分これは誰かを助けるかもしれません。

StreamReaderは、破棄されるときに接続を閉じます。ただし、StreamReader / StreamWriterで「using(Stream stream = ...){...}」を使用すると、Streamが2回破棄される可能性があります。(1)StreamReaderオブジェクトが破棄されるとき(2)およびStream usingブロックが破棄されるとき閉じます。これにより、VSのコード分析を実行すると、CA2202警告が表示されます。

CA2202ページから直接取得した別のソリューションは、try / finallyブロックを使用することです。正しくセットアップしてください。これにより、接続が一度だけ閉じられます。

マイクロソフトはCA2202の下部近くで、以下を使用することをお勧めします。

Stream stream = null;
try
{
    stream = new FileStream("file.txt", FileMode.OpenOrCreate);
    using (StreamWriter writer = new StreamWriter(stream))
    {
        stream = null;
        // Use the writer object...
    }
}
finally
{
    if(stream != null)
        stream.Dispose();
}

の代わりに...

// Generates a CA2202 warning
using (Stream stream = new FileStream("file.txt", FileMode.Open))
using (XmlReader reader = new XmlReader (stream))
{
    // Use the reader object...
}

2
警告は、受け入れられた回答のコメントでも説明されています。Jon Skeetがそこでアドバイスを提供しています。
Marcin 2017

また、usingステートメントは実際にはコンパイラーによってtry-finallyブロックに変換されることに注意してください。
ジェイソンケリー

2

はい。Dispose()を呼び出し、IDisposableを呼び出すと( "using"が行います)、オブジェクトはすべてのリソースをクリーンアップします。これには、ファイル記述子をフラッシュおよびクローズするストリームが含まれます。

あなたのケースでそれを他のメソッドに渡したい場合は、それらのメソッドがusingブロックで読み取り/書き込みを行わないことを確認する必要があります。



弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.