Должен ли я вызывать Close() или Dispose() для объектов stream?

классы, такие как Stream, StreamReader, StreamWriter etc реализует 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() способ:

это хорошо?

4 ответов


быстрый прыжок в Reflector.NET показывает, что Close() метод on 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();
}

это не влияет на поведение кода, но это помогает читаемости.


нет, вы не должны вызывать эти методы вручную. В конце using block автоматически вызывается метод Dispose, который позаботится о том, чтобы освободить неуправляемые ресурсы (по крайней мере, для стандартных классов .NET BCL, таких как streams, readers/writers,...). Таким образом, вы также можете написать свой код следующим образом:

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.


в документации говорится, что эти два метода эквивалентны:

StreamReader.Закрыть: эта реализация Close вызывает метод Dispose, передающий истинное значение.

модулю записи StreamWriter.Закрыть: Эта реализация Close вызывает метод Dispose, передающий значение true.

поток.Закрыть: этот метод вызывает 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, два вызова будут эквивалентны. Однако в некоторых классах можно повторно открыть объект, который был закрыт. Некоторые такие классы могут поддерживать некоторые ресурсы после закрытия, чтобы разрешить повторное открытие; другие могут не поддерживать какие-либо ресурсы на закрытии, но могут установить флаг на Dispose, чтобы явно запретить повторное открытие.

договор на IDisposable.Dispose явно требует, чтобы вызов его на объект, который никогда не будет использоваться снова, будет в худшем случае безвреден, поэтому я бы рекомендовал вызвать либо IDisposable.Dispose или метод Dispose для каждого объекта IDisposable, независимо от того, вызывает ли он также Close.