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

Нужно ли обрезать StreamWriter в блоке использования?

Несколько дней назад я опубликовал такой код:

StreamWriter writer = new StreamWriter(Response.OutputStream);
writer.WriteLine("col1,col2,col3");
writer.WriteLine("1,2,3");
writer.Close();
Response.End();

Мне сказали, что вместо этого я должен обернуть StreamWriter в используемом блоке в случае исключений. Такое изменение заставит его выглядеть так:

using(StreamWriter writer = new StreamWriter(Response.OutputStream))
{
    writer.WriteLine("col1,col2,col3");
    writer.WriteLine("1,2,3");
    writer.Close(); //not necessary I think... end of using block should close writer
}
Response.End();

Я не уверен, почему это ценное изменение. Если исключение произошло без использования блока, писатель и ответ все равно будут очищены, верно? Что меня использует блок использования?

4b9b3361

Ответ 1

В первом примере поток останется открытым, так как ошибка будет отрицать его закрытие.

Оператор using принудительно вызывает вызов Dispose(), который должен очистить объект и закрыть все открытые соединения, когда он выходит из блока.

Ответ 2

Я собираюсь дать особое мнение. Ответ на конкретный вопрос: "Необходимо ли обернуть StreamWriter в блоке использования?" на самом деле Нет. На самом деле вы не должны вызывать Dispose в StreamWriter, потому что его Dispose плохо спроектирован и делает неправильную вещь.

Проблема с StreamWriter заключается в том, что при ее удалении он предоставляет базовый поток. Если вы создали StreamWriter с именем файла, и он создал свой собственный FileStream внутренне, то это поведение было бы вполне уместным. Но если, как здесь, вы создали StreamWriter с существующим потоком, то это поведение абсолютно неверно (tm). Но это все равно.

Код, подобный этому, не будет работать:

var stream = new MemoryStream();
using (var writer = new StreamWriter(stream)) { ... }
stream.Position = 0;
using (var reader = new StreamReader(stream)) { ... }

потому что, когда блок StreamWriter using Устанавливает StreamWriter, который, в свою очередь, выкинет поток. Поэтому, когда вы пытаетесь читать из потока, вы получаете ObjectDisposedException.

StreamWriter - это ужасное нарушение правила "очистить свой собственный беспорядок". Он пытается очистить кого-то еще от беспорядка, хотят ли они этого или нет.

(Представьте, если вы попробовали это в реальной жизни. Попробуйте объяснить полицейским, почему вы ворвались в чужой дом и начали бросать все их вещи в корзину...)

По этой причине я считаю StreamWriter (и StreamReader, который делает то же самое), чтобы быть среди очень немногих классов, где "если он реализует IDisposable, вы должны вызвать Dispose", это неправильно. Никогда не вызывайте Dispose в StreamWriter, который был создан в существующем потоке. Вместо этого вызовите Flush().

Затем просто убедитесь, что вы очищаете Stream, когда хотите. (Как отметил Джо, ASP.NET предоставляет вам Response.OutputStream, поэтому вам здесь не нужно беспокоиться.)

Предупреждение: если вы не удаляете StreamWriter, вам нужно позвонить Flush(), когда вы закончите писать. В противном случае у вас могут быть данные, все еще буферизированные в памяти, которые никогда не попадают в выходной поток.

Мое правило для StreamReader заключается в том, что он не реализует IDisposable. Просто отпустите его, когда закончите.

Мое правило для StreamWriter - это вызов Flush, в который вы иначе могли бы позвонить Dispose. (Это означает, что вы должны использовать try.. finally вместо using.)

Ответ 3

Если исключение происходит без использования блока и убивает программу, вы останетесь с openconnections. Блок использования всегда будет закрывать соединение для вас, аналогично, если вы должны использовать try {} catch {} finally {}

Ответ 4

В конце концов, писатель будет очищен. Когда это произойдет, до сборщика мусора, который заметит, что Dispose для команды не был вызван и вызывает его. Конечно, GC не может работать в течение нескольких минут, часов или дней в зависимости от ситуации. Если у автора есть эксклюзивный замок, скажем, файл, ни один другой процесс не сможет его открыть, хотя вы уже давно закончили.

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

Ответ 5

Обтекание StreamWriter в блоке using в значительной степени эквивалентно следующему коду:

StreamWriter writer;
try
{
    writer = new StreamWriter(Response.OutputStream);
    writer.WriteLine("col1,col2,col3");
    writer.WriteLine("1,2,3");
}
catch
{
    throw;
}
finally
{
    if (writer != null)
    {
        writer.Close();    
    }
}

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

Ответ 6

По-моему, необходимо обернуть любой класс, который реализует IDisposable в блоке using. Тот факт, что класс реализует IDisposable, означает, что у класса есть ресурсы, которые необходимо очистить.

Ответ 7

Мое правило состоит в том, что если я вижу Dispose, указанный в intellisense, я переношу его в используемый блок.

Ответ 8

использование блоков вызывает dispose(), когда он заканчивается. Это просто удобный способ обеспечить своевременную очистку ресурсов.

Ответ 9

В почти каждом случае, если класс реализует IDisposable, и если вы создаете экземпляр этого класса, то вам нужен блок using.

Ответ 10

В то время как хорошая практика всегда дипотирует одноразовые классы, такие как StreamWriter, как указывают другие, в этом случае это не имеет значения.

Response.OutputStream будет утилизирован инфраструктурой ASP.NET, когда он завершит обработку вашего запроса.

StreamWriter предполагает, что он "владеет" потоком, переданным конструктору, и поэтому будет закрывать поток при его размещении. Но в примере, который вы предоставили, поток был создан вне вашего кода, так что будет другой владелец, ответственный за очистку.