Должен ли я вызвать Close() или Dispose() для объектов потока?
Классы, такие как Stream
, StreamReader
, StreamWriter
и т.д. реализуют интерфейс 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()
:
Это хорошо?
Ответы
Ответ 1
Быстрый переход в Reflector.NET показывает, что метод Close()
на StreamWriter
:
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 ( ... ) { ... }
всегда следует использовать, когда это возможно, поскольку это помогает вам "не работать с ножницами".
Но, хотя это помогает в правильности, оно уменьшает читаемость. В С# у нас уже есть множество закрывающих фигурных скобок, и как мы узнаем, какая из них фактически выполняет закрытие потока?
Поэтому я думаю, что лучше всего сделать это:
using (var stream = ...)
{
/* code */
stream.Close();
}
Это не влияет на поведение кода, но оно помогает читать.
Ответ 2
Нет, вы не должны вызывать эти методы вручную. В конце блока 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.
Ответ 3
В документации говорится, что эти два метода эквивалентны:
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();
}
Лично я бы придерживался первого варианта, так как он содержит меньше "шума".
Ответ 4
Во многих классах, которые поддерживают методы Close и Dispose, оба вызова будут эквивалентны. Однако на некоторых классах можно повторно открыть объект, который был Close'd. Некоторые из таких классов могут сохранить некоторые ресурсы после закрытия, чтобы разрешить повторное открытие; другие могут не поддерживать какие-либо ресурсы в Close, но могут установить флаг в Dispose, чтобы явно запретить повторное открытие.
Контракт для IDisposable.Dispose явно требует, чтобы вызов его на объект, который никогда не будет использоваться снова, будет в худшем случае безвредным, поэтому я бы рекомендовал вызывать либо IDisposable.Dispose, либо метод Dispose на каждом объекте IDisposable, независимо от того, не один также вызывает Close.