ストリームオブジェクトに対してClose()またはDispose()を呼び出す必要がありますか?


151

以下のようなクラスStreamStreamReaderStreamWriterなどの実装IDisposableインタフェース。つまりDispose()、これらのクラスのオブジェクトでメソッドを呼び出すことができます。彼らはpublicと呼ばれるメソッドも定義しましたClose()。オブジェクトの処理が完了したら、何を呼び出せばよいのでしょうか。両方とも呼ぶとどうなりますか?

私の現在のコードはこれです:

using (Stream responseStream = response.GetResponseStream())
{
   using (StreamReader reader = new StreamReader(responseStream))
   {
      using (StreamWriter writer = new StreamWriter(filename))
      {
         int chunkSize = 1024;
         while (!reader.EndOfStream)
         {
            char[] buffer = new char[chunkSize];
            int count = reader.Read(buffer, 0, chunkSize);
            if (count != 0)
            {
               writer.Write(buffer, 0, count);
            }
         }
         writer.Close();
      }
      reader.Close();
   }
}

ご覧のとおり、各オブジェクトのメソッドusing()を自動的に呼び出す構造を作成しましたDispose()。しかし、Close()メソッドも呼び出します。正しいですか?

ストリームオブジェクトを使用する際のベストプラクティスを教えてください。:-)

MSDNの例ではusing()構成を使用せず、Close()メソッドを呼び出します。

いいですか?


ReSharperを使用している場合、これをパターンカタログ内の「アンチパターン」として定義できます。ReSharperは、定義に関する各使用をエラー/ヒント/警告としてマークします。ReSharperがそのような発生に対してQuickFixを適用する方法を定義することも可能です。
Thorsten Hans

3
ほんのヒント:複数の使い捨てイテンスに対して、そのようなusingステートメントを使用できます:(Stream responseStream = response.GetResponseStream())using(StreamReader reader = new StreamReader(responseStream))using(StreamWriter writer = new StreamWriter(filename)) {//...Some code}
Latrova


それらを互いの上に積み重ねて、ブラケットのセットを1つ持つことができるように、usingステートメントをネストする必要はありません。:別のポストでは、私はあなたの「コードの矢印」を見て、修正したい場合はその技術との文を使用して持っていたはずですコードスニペットの編集提案stackoverflow.com/questions/5282999/...
ティモシー・ゴンザレス

2
@ Suncat2000複数のusingステートメントを使用できますが、それらをネストせずにスタックします。タイプを制限する次のような構文を意味するわけではありませんusing (MemoryStream ms1 = new MemoryStream(), ms2 = new MemoryStream()) { }。:あなたがタイプ再定義することができる場所私はこのような意味using (MemoryStream ms = new MemoryStream()) using (FileStream fs = File.OpenRead("c:\\file.txt")) { }
ティモシー・ゴンザレス

回答:


101

Reflector.NETにすばやくジャンプすると、Close()メソッドStreamWriterは次のとおりです。

public override void Close()
{
    this.Dispose(true);
    GC.SuppressFinalize(this);
}

そしてStreamReader

public override void Close()
{
    this.Dispose(true);
}

Dispose(bool disposing)オーバーライドStreamReaderは次のとおりです。

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;
            /* deleted for brevity */
            base.Dispose(disposing);
        }
    }
}

StreamWriterこの方法は同様です。

したがって、コードを読むと、ストリームに対してClose()Dispose()を好きなだけ何度でも任意の順序で呼び出すことができることは明らかです。動作に変化はありません。

したがって、使用するのがより読みやすいかどうかDispose()Close()および/またはusing ( ... ) { ... }

私の個人的な好みは、using ( ... ) { ... }「はさみで走らない」ことを助けるので、可能な限り常に使用することです。

しかし、これは正確さを助けますが、読みやすさを低下させます。C#には既に中かっこがたくさんあるので、実際に中かっこでストリームを閉じているかどうかを確認するにはどうすればよいでしょうか。

だから私はこれを行うのが最善だと思います:

using (var stream = ...)
{
    /* code */

    stream.Close();
}

コードの動作には影響しませんが、読みやすさを向上させます。


20
" C#では既に中かっこが大量にあるので、実際に中かっこでストリームを閉じているかどうかを知るにはどうすればよいですか? "これは大きな問題ではないと思います:ストリームは "適切なタイミングで"閉じられます、つまり、変数がスコープ外になり、不要になったとき。
ハインツィ

110
うーん、いや、それは「一体なぜ彼はそれを二度閉じているのですか?」読み込み中のスピードバンプ。
ハンスパッサント2011

57
私は冗長なClose()呼び出しに同意しません。経験の浅い誰かがコードをusing見て知らない場合は、1)コードを調べて学習するか、2)盲目的にClose()手動で追加します。彼が2)を選択した場合、おそらく他の開発者が冗長を見てClose()、「笑い」の代わりに、経験の少ない開発者に指示します。私は経験の浅い開発者にとって生活を困難にするのではなく、経験豊富な開発者に変えることを望んでいます。
R.マルティーニョフェルナンデス

14
+ Close()を使用して/ analyzeをオンにすると、「警告:CA2202:Microsoft.Usage:オブジェクト 'f'はメソッド 'Foo(string)'で複数回破棄できます。システムの生成を回避します。 ObjectDisposedExceptionオブジェクトでDisposeを複数回呼び出すことはできません。:Lines:41 "したがって、現在の実装はCloseおよびDisposeの呼び出しで問題ありませんが、ドキュメントと/ analyzeによると、問題があり、将来のバージョンで変更される可能性があります。ネット。
marc40000

4
良い答えは+1です。考慮すべきもう一つ。// Closeのように右中かっこの後にコメントを追加しないでください。または、初心者であるため、明確ではない右中かっこの後に1行を追加します。たとえば、長いクラスの場合、最後の右中括弧の後に// End Namespace XXXを追加し、2番目の最後の右中括弧の後に// End Class YYYを追加します。これはコメントの対象ではありませんか。ちょっと興味があるんだけど。:)初心者として、私はそのようなコードを見ました。「なぜ2回目のクローズが必要なのか」という質問をしました。コードの余分な行がわかりやすくなるような感じはしません。ごめんなさい。
フランシスロジャース

51

いいえ、これらのメソッドを手動で呼び出すことはできません。usingブロックの最後で、Dispose()メソッドが自動的に呼び出され、アンマネージリソースを解放します(少なくとも、ストリーム、リーダー/ライターなどの標準.NET BCLクラスの場合)。したがって、次のようなコードを書くこともできます。

using (Stream responseStream = response.GetResponseStream())
    using (StreamReader reader = new StreamReader(responseStream))
        using (StreamWriter writer = new StreamWriter(filename))
        {
            int chunkSize = 1024;
            while (!reader.EndOfStream)
            {
                 char[] buffer = new char[chunkSize];
                 int count = reader.Read(buffer, 0, chunkSize);
                 if (count != 0)
                 {
                     writer.Write(buffer, 0, count);
                 }
            }
         }

Close()メソッド呼び出しDispose()


1
リーダーが破棄されたときに確実に閉じられるようにラップされているためusing、最初にする必要はないでしょう。それにもかかわらず+1responseStreamreader
Isak Savo

これはあなたが言ったとき混乱しますThe Close method calls Dispose....そしてあなたの投稿の残りの部分では、それDispose()がを呼び出すだろうことを暗示していますClose()、私は後者を手動で呼び出すべきではありません。彼らはお互いを呼ぶと言っているのですか?
Nawaz、2011

@Nawaz、私の投稿は混乱していた。Closeメソッドは、単にDisposeを呼び出します。あなたの場合、アンマネージリソースを解放するためにDisposeが必要です。usingステートメントでコードをラップすることにより、Disposeメソッドが呼び出されます。
Darin Dimitrov

3
ひどい答え。usingブロックを使用できることを前提としています。私は時々書くクラスを実装しているのでできません。
Jez

5
@Jez次に、クラスはIDisposableインターフェイスを実装する必要があります。また、closeが領域内の標準的な用語である場合は Close()も実装する必要があります。これにより、クラスを使用するクラスで使用できるようになりますusing(または、Disposeパターンに進みます)。
Dorus

13

ドキュメントには、これら2つの方法は同等であると記載されています。

StreamReader.Close:このCloseの実装は、true値を渡してDisposeメソッドを呼び出します。

StreamWriter.Close:このCloseの実装は、true値を渡してDisposeメソッドを呼び出します。

Stream.Close:このメソッドはDisposeを呼び出し、trueを指定してすべてのリソースを解放します。

したがって、これらはどちらも等しく有効です。

/* Option 1, implicitly calling Dispose */
using (StreamWriter writer = new StreamWriter(filename)) { 
   // do something
} 

/* Option 2, explicitly calling Close */
StreamWriter writer = new StreamWriter(filename)
try {
    // do something
}
finally {
    writer.Close();
}

個人的には、「ノイズ」が少ないので、最初のオプションを使用します。


5

Close()Dispose()メソッドの両方をサポートする多くのクラスでは、2つの呼び出しは同等です。ただし、一部のクラスでは、閉じられたオブジェクトを再度開くことができます。そのようなクラスの中には、再オープンを許可するために、クローズ後もいくつかのリソースを存続させるものがあります。他のユーザーはでリソースを有効に保つことはできませんClose()が、Dispose()再オープンを明示的に禁止するフラグをオンに設定することがあります。

の規約IDisposable.Disposeでは、二度と使用されないオブジェクトで呼び出すことは最悪の無害であることを明示的に要求しているため、を呼び出すかどうかにかかわらず、すべてのオブジェクトでかIDisposable.Disposeメソッドを呼び出すことをお勧めします。Dispose()IDisposableClose()


参考までに、閉じると破棄の楽しみを説明するMSDNブログの記事をご覧ください。blogs.msdn.com/b/kimhamil/archive/2008/03/15/…
JamieSee

1

これは古い質問ですが、今では、それぞれをブロックする必要なしにusingステートメントを書くことができます。収容ブロックが完成すると、逆の順序で廃棄されます。

using var responseStream = response.GetResponseStream();
using var reader = new StreamReader(responseStream);
using var writer = new StreamWriter(filename);

int chunkSize = 1024;
while (!reader.EndOfStream)
{
    char[] buffer = new char[chunkSize];
    int count = reader.Read(buffer, 0, chunkSize);
    if (count != 0)
    {
        writer.Write(buffer, 0, count);
    }
}

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using

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