Выполняет Stream.Dispose всегда вызывает Stream.Закрыть (и Stream.Сбросить)

StackOverflow https://stackoverflow.com/questions/911408

  •  06-09-2019
  •  | 
  •  

Вопрос

Если у меня возникнет следующая ситуация:

StreamWriter MySW = null;
try
{
   Stream MyStream = new FileStream("asdf.txt");
   MySW = new StreamWriter(MyStream);
   MySW.Write("blah");
}
finally
{
   if (MySW != null)
   {
      MySW.Flush();
      MySW.Close();
      MySW.Dispose();
   }
}

Могу я просто позвонить MySW.Dispose() и пропустить Закрытие, даже если оно предусмотрено?Существуют ли какие-либо реализации потока, которые работают не так, как ожидалось (например, CryptoStream)?

Если нет, то является ли следующий просто плохим кодом:

using (StreamWriter MySW = new StreamWriter(MyStream))
{
   MySW.Write("Blah");
}
Это было полезно?

Решение

Могу ли я просто вызвать MySW.Dispose() и пропустить закрытие, даже если оно предусмотрено ?

Да, именно для этого он и предназначен.

Существуют ли какие-либо реализации Stream , которые работают не так, как ожидалось (например, CryptoStream)?

Можно с уверенностью предположить, что если объект реализует IDisposable, он будет распоряжаться собой должным образом.

Если это не так, то это было бы ошибкой.

Если нет, то является ли следующее просто плохим код:

Нет, этот код является рекомендуемым способом работы с объектами, которые реализуют IDisposable.

Более подробная информация содержится в принятом ответе на Закрыть и утилизировать - что вызывать?

Другие советы

Я использовал Reflector и обнаружил, что System.IO.Stream.Dispose выглядит примерно так:

public void Dispose()
{
    this.Close();
}

Как упоминал Дэниел Брукнер, Dispose и Close - это фактически одно и то же.

Однако Stream НЕ вызывает Flush(), когда он удален / закрыт.FileStream (и я предполагаю, что любой другой поток с механизмом кэширования) вызывает Flush() при удалении.

Если вы расширяете Stream, или MemoryStream и т.д.вам нужно будет реализовать вызов Flush() при удалении / закрытии, если это необходимо.

Оба StreamWriter.Dispose() и Stream.Dispose() освобождают все ресурсы, удерживаемые объектами.Оба они закрывают базовый поток.

Исходный код Stream.Dispose() (обратите внимание, что это детали реализации, поэтому не полагайтесь на это):

public void Dispose()
{
    this.Close();
}

StreamWriter.Dispose() (то же, что и с Stream.Dispose()):

protected override void Dispose(bool disposing)
{
    try
    {
        // Not relevant things
    }
    finally
    {
        if (this.Closable && (this.stream != null))
        {
            try
            {
                if (disposing)
                {
                    this.stream.Close();
                }
            }
            finally
            {
                // Not relevant things
            }
        }
    }
}

Тем не менее, я обычно неявно закрываю streams / streamwriters перед их удалением - я думаю, это выглядит чище.

Все стандартные потоки (FileStream, CryptoStream) будут пытаться сбросить данные при закрытии / удалении.Я думаю, вы можете положиться на это при любых реализациях Microsoft stream.

В результате Close /Dispose может выдать исключение, если сброс завершится неудачей.

На самом деле, IIRC в реализации FileStream .NET 1.0 была ошибка, заключающаяся в том, что она не смогла бы освободить дескриптор файла, если бы сброс вызывал исключение.Это было исправлено в .NET 1.1 путем добавления блока try / finally к методу Dispose(boolean).

Для объектов, которые необходимо закрыть вручную, следует приложить все усилия для создания объекта в блоке using.

//Cannot access 'stream'
using (FileStream stream = File.Open ("c:\\test.bin"))
{
   //Do work on 'stream'
} // 'stream' is closed and disposed of even if there is an exception escaping this block
// Cannot access 'stream' 

Таким образом, никто никогда не сможет неправильно получить доступ к 'stream' вне контекста предложения using, и файл всегда будет закрыт.

Я посмотрел в исходном коде .net для класса Stream , в нем было следующее, что наводит на мысль, что да, вы можете...

    // Stream used to require that all cleanup logic went into Close(),
    // which was thought up before we invented IDisposable.  However, we 
    // need to follow the IDisposable pattern so that users can write
    // sensible subclasses without needing to inspect all their base
    // classes, and without worrying about version brittleness, from a
    // base class switching to the Dispose pattern.  We're moving 
    // Stream to the Dispose(bool) pattern - that's where all subclasses
    // should put their cleanup starting in V2. 
    public virtual void Close() 
    {
        Dispose(true); 
        GC.SuppressFinalize(this);
    }

    public void Dispose() 
    {
        Close(); 
    } 

Stream.Close реализуется вызовом к Stream.Dispose или наоборот - таким образом, методы эквивалентны. Stream.Close существует только потому, что закрытие потока звучит более естественно, чем удаление потока.

Кроме того, вам следует стараться избегать явных вызовов этих методов и использовать using заявление вместо этого, чтобы бесплатно получить правильную обработку исключений.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top