Stream
、StreamReader
、StreamWriter
などのクラスは、IDisposable
インターフェイスを実装します。つまり、これらのクラスのオブジェクトでDispose()
メソッドを呼び出すことができます。また、Close()
というpublic
メソッドも定義しています。今、私は混乱しています。オブジェクトを使い終わったら、何を呼ぶべきでしょうか?両方に電話したらどうなりますか?
私の現在のコードはこれです:
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()
メソッドを呼び出します。
いいですか?
Reflector.NETに簡単にジャンプすると、StreamWriter
のClose()
メソッドは次のようになります。
public override void Close()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
また、StreamReader
は次のとおりです。
public override void Close()
{
this.Dispose(true);
}
StreamReader
のDispose(bool disposing)
オーバーライドは次のとおりです。
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();
}
コードの動作には影響しませんが、読みやすさは向上します。
いいえ、これらのメソッドを手動で呼び出すべきではありません。 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を呼び出します。
ドキュメントには、これら2つの方法は同等であると書かれています。
StreamReader.Close :このCloseの実装は、真の値を渡してDisposeメソッドを呼び出します。
StreamWriter.Close :このCloseの実装は、真の値を渡してDisposeメソッドを呼び出します。
Stream.Close :このメソッドはDisposeを呼び出し、すべてのリソースを解放するためにtrueを指定します。
したがって、これらは両方とも同等に有効です。
/* Option 1 */
using (StreamWriter writer = new StreamWriter(filename)) {
// do something
}
/* Option 2 */
StreamWriter writer = new StreamWriter(filename)
try {
// do something
}
finally {
writer.Close();
}
個人的には、「ノイズ」が少ないため、最初のオプションに固執します。
CloseメソッドとDisposeメソッドの両方をサポートする多くのクラスでは、2つの呼び出しは同等です。ただし、一部のクラスでは、クローズされたオブジェクトを再度開くことができます。そのようなクラスの中には、再オープンを許可するために、Closeの後に一部のリソースを存続させているものがあります。他のユーザーは、Closeでリソースを保持しませんが、Disposeにフラグを設定して、再オープンを明示的に禁止する場合があります。
IDisposable.Disposeのコントラクトでは、二度と使用されないオブジェクトで明示的に呼び出すことは最悪の場合無害であることが明示的に要求されているため、IDisposable.DisposeまたはすべてのIDisposableオブジェクトでDisposeというメソッドを呼び出すことをお勧めします。 Closeを呼び出します。