Чи закриває потік читання закриває потік?


166

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

Я б надіслав BinaryReader / Writer, але я також використовую StreamReader (можливо, я повинен обійти це. Я використовую це лише для GetLine та ReadLine). Це досить клопітно, якщо він закриває потік кожного разу, коли письменник / читач закривається.

Відповіді:


204

Так, StreamReader, StreamWriter, BinaryReaderі BinaryWriterвсе близько / утилізувати їх основні потоки при виклику Disposeна них. Вони не розпоряджаються потоком, якщо читач / письменник є лише зібраним сміттям - завжди слід утилізувати читача / письменника, бажано із usingзаявою. (Насправді, жоден із цих класів не має фіналізаторів, а також не повинен.)

Особисто я вважаю за краще мати також оператор використання для потоку. Ви можете вкладати usingзаяви без дужок досить акуратно:

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

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


2
о, добре, це відбувається лише при виклику розпорядження, а не тоді, коли нібито доопрацьовується.
Нефзен

1
@Nefzen: Це тому, що немає гарантії того, який порядок будуть завершені. Якщо і StreamReader, і базовий потік мають право на завершення, GC може допрацювати спочатку потік - тоді streamreader не матиме посилання на потік. З цієї причини ви можете випускати некеровані ресурси лише під час завершення (наприклад, 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.Чи варто це просто ігнорувати? Я не мав поки жодних винятків ...
НВ

15
@HB: У цьому випадку це безпечно ігнорувати. Або ви можете просто створити потік у виклику конструктора StreamReader. Попередження виглядає неправдивим для мене, враховуючи, що в документах IDisposable.Disposeпрямо вказано: "Якщо метод Dispose об'єкта викликається більше одного разу, об'єкт повинен ігнорувати всі виклики після першого. Об'єкт не повинен кидати виняток, якщо його метод Dispose є називається кілька разів ».
Джон Скіт

5
@JonSkeet: Насправді є сторінка для цього , ти був прав , це хибно :)
НВ

45

Це стара, але я хотів сьогодні зробити щось подібне і виявив, що все змінилося. Оскільки .net 4.5 є leaveOpenаргумент:

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

Єдина проблема полягає в тому, що не зовсім очевидно, що встановити для інших параметрів. Ось деяка допомога:

Від на сторінці MSDN для StreamReader Конструктора (потік):

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

Це просто залишає detectEncodingFromByteOrderMarksякий судячи з вихідний код єtrue

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

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

Було б добре, якби деякі з цих замовчувань були викриті або якщо аргументи були необов'язковими, щоб ми могли просто вказати ті, які ми хочемо.


Дуже приємна інформація! Ніколи не чув про цей новий параметр, і він насправді має багато сенсу.
julealgon

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 using закривається. Це призводить до попередження CA2202 при запуску аналізу коду VS.

Ще одне рішення, взяте безпосередньо зі сторінки CA2202 , полягає у використанні блоку спробу / нарешті. Якщо встановити правильно, це з'єднання закриє лише один раз.

Майже в нижній частині 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...
}

2
Попередження обговорюється також у коментарях до прийнятої відповіді. Джон Скіт пропонує там кілька порад.
Marcin

Також майте на увазі, що використовуючий оператор перетворюється компілятором у блок спробу.
Джейсон Келлі

2

Так. Виклик Dispose () on і IDisposable (що "за допомогою" робить) повинен об'єкт очистити всі його ресурси. Сюди входять потоки промивання та закриття дескрипторів файлів.

Якщо у вашому випадку ви хочете передати їх іншим методам, вам потрібно переконатися, що ці методи не виконують їх читання / запис у використанні блоку.



-2

Потік розміщений або за допомогою ключового слова "з використанням", або виклику розпоряджається явно

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.