Подтвердить что ты не робот

Должен ли я вызвать 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():

Это хорошо?

4b9b3361

Ответ 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.