Утилизирует ли Streamreader поток?

166

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

Я бы отправил BinaryReader / Writer, но я также использую StreamReader (возможно, мне следует обойти это. Я использую это только для GetLine и ReadLine). Это довольно хлопотно, если он закрывает поток каждый раз, когда писатель / читатель закрывается.

Nefzen
источник

Ответы:

204

Да, StreamReader, StreamWriter, BinaryReaderи BinaryWriterвсе близко / утилизировать их основные потоки при вызове Disposeна них. Они не удаляют поток, если читатель / писатель просто собирает мусор - вы всегда должны утилизировать читателя / писателя, желательно с usingутверждением. (На самом деле ни у одного из этих классов нет финализаторов, и при этом они не должны быть.)

Лично я предпочитаю иметь оператор использования для потока. Вы можете usingаккуратно вкладывать утверждения без фигурных скобок:

using (Stream stream = ...)
using (StreamReader reader = new StreamReader(stream, Encoding.Whatever))
{
}

Несмотря на то, что usingоператор для потока несколько избыточен (если StreamReaderконструктор не выдает исключение), я считаю, что лучше всего, если вы избавитесь от него StreamReaderи просто используете поток непосредственно позднее, у вас уже будет правильное распоряжение семантика.

Джон Скит
источник
2
о, хорошо, это происходит только при вызове Dispose, а не когда якобы завершается.
Нефзен
1
@Nefzen: Это потому, что нет никакой гарантии, в каком порядке будут завершены ваши объекты. Если как StreamReader, так и нижележащий Stream имеют право на завершение, GC может сначала завершить поток - тогда потоковый считыватель не будет иметь ссылку на поток. По этой причине вы можете высвобождать неуправляемые ресурсы только в рамках завершения (например, FileStream закрывает свой дескриптор файла Windows в своем завершении). О, и, конечно, если вы никогда не утилизируете, поток все равно будет в конечном итоге собран (и файл закрыт). Это просто очень плохая практика - не выбрасывать поток.
JMarsch
13
Это вложение заставляет анализатор кода VS жаловаться: CA2202 : Microsoft.Usage : Object 'stream' can be disposed more than once in method '...'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.это просто игнорировать? До сих пор я не получил никаких исключений ...
HB
15
@HB: это безопасно игнорировать в этом случае. Или вы можете просто создать поток в вызове конструктора StreamReader. Предупреждение выглядит поддельным для меня, учитывая, что документы IDisposable.Disposeявно указывают: «Если метод Dispose объекта вызывается более одного раза, объект должен игнорировать все вызовы после первого. Объект не должен вызывать исключение, если его метод Dispose звонил несколько раз.
Джон Скит
5
@JonSkeet: На самом деле есть страница для этого , вы были правы, это фальшивка :)
HB
45

Это старый, но я хотел сделать что-то подобное сегодня и обнаружил, что все изменилось. Начиная с .net 4.5, есть leaveOpenаргумент:

public StreamReader( Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen )

Единственная проблема заключается в том, что не совсем очевидно, что устанавливать для других параметров. Вот некоторая помощь:

Со страницы msdn для Конструктора StreamReader (Stream):

Этот конструктор инициализирует кодирование для UTF8Encoding, свойство BaseStream с использованием параметра stream и размер внутреннего буфера до 1024 байтов.

Это просто оставляет detectEncodingFromByteOrderMarksкоторый судя по исходный код являетсяtrue

public StreamReader(Stream stream)
        : this(stream, true) {
}

public StreamReader(Stream stream, bool detectEncodingFromByteOrderMarks)
        : this(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize) {
}

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

acarlon
источник
Очень хорошая информация! Никогда не слышал об этом новом параметре, и это действительно имеет большой смысл.
Jullealgon
3
Для ленивых людей, таких как я, короткий ответ, чтобы оставить поток открытым, будет таким:using (var streamReader = new StreamReader(myStream, Encoding.UTF8, true, 1024, true))
beawolf
29

Да, это так. Вы можете убедиться в этом, посмотрев реализацию с помощью Reflector.

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;    
            this.encoding = null;
            this.decoder = null;
            this.byteBuffer = null;
            this.charBuffer = null;
            this.charPos = 0;
            this.charLen = 0;
            base.Dispose(disposing);
        }
    }
}
Брайан Расмуссен
источник
13

Шесть лет спустя, но, может быть, это кому-нибудь поможет.

StreamReader закрывает соединение, когда оно удаляется. Однако «использование (Stream stream = ...) {...}» с StreamReader / StreamWriter может привести к тому, что поток будет удален дважды: (1) когда объект StreamReader удаляется (2) и когда блок Stream используя закрывается. Это приводит к предупреждению CA2202 при выполнении анализа кода VS.

Другое решение, взятое непосредственно со страницы CA2202 , заключается в использовании блока try / finally. Настройте правильно, это только один раз закроет соединение.

В нижней части CA2202 Microsoft рекомендует использовать следующее:

Stream stream = null;
try
{
    stream = new FileStream("file.txt", FileMode.OpenOrCreate);
    using (StreamWriter writer = new StreamWriter(stream))
    {
        stream = null;
        // Use the writer object...
    }
}
finally
{
    if(stream != null)
        stream.Dispose();
}

вместо того...

// Generates a CA2202 warning
using (Stream stream = new FileStream("file.txt", FileMode.Open))
using (XmlReader reader = new XmlReader (stream))
{
    // Use the reader object...
}
Sunsetquest
источник
2
Предупреждение также обсуждается в комментариях к принятому ответу. Джон Скит предлагает несколько советов там.
Марцин
Также помните, что оператор using фактически преобразуется компилятором в блок try-finally.
Джейсон Келли
2

Да. Вызов Dispose () on и IDisposable (что делает «using») должен заставить объект очистить все свои ресурсы. Это включает в себя очистку потоков и закрытие их файловых дескрипторов.

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

Джо М
источник
-2

поток удаляется либо с помощью ключевого слова "using", либо с помощью явного вызова dispose

Ахмед Саид
источник