Должен ли я вызвать Close () или Dispose () для потоковых объектов? - PullRequest
136 голосов
/ 23 сентября 2011

Классы, такие как 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():

Это хорошо?

Ответы [ 4 ]

96 голосов
/ 23 сентября 2011

Быстрый переход в 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();
}

Это не влияет на поведение кода, но способствует удобочитаемости.

45 голосов
/ 23 сентября 2011

Нет, вы не должны вызывать эти методы вручную.В конце блока 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.

12 голосов
/ 23 сентября 2011

В документации сказано, что эти два метода эквивалентны:

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 голосов
/ 23 сентября 2011

Во многих классах, которые поддерживают методы Close и Dispose, оба вызова будут эквивалентны.Однако в некоторых классах можно повторно открыть объект, который был Close'd.Некоторые такие классы могут поддерживать некоторые ресурсы после закрытия, чтобы разрешить повторное открытие;другие могут не поддерживать какие-либо ресурсы в Close, но могут установить флаг Dispose, чтобы явно запретить повторное открытие.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...