Czy pozbycie się czytnika strumieniowego zamyka strumień?


166

Wysyłam strumień do metod do pisania, aw tych metodach używam binarnego czytnika / wrtier. Kiedy czytelnik / piszący zostanie usunięty, przez usinglub tylko wtedy, gdy nie ma do niego odniesienia, czy strumień również jest zamknięty?

Chciałbym wysłać BinaryReader / Writer, ale używam też StreamReader (może powinienem to obejść. Używam tego tylko dla GetLine i ReadLine). Jest to dość kłopotliwe, jeśli zamyka strumień za każdym razem, gdy zostaje zamknięty autor / czytelnik.

Odpowiedzi:


204

Tak StreamReader, StreamWriter, BinaryReadera BinaryWriterwszystko blisko / wyrzucać ich podstawowych strumienie podczas wywoływania Disposena nich. Oni nie wyrzucać strumienia jeśli czytelnik / pisarz jest po prostu śmieci zebrane choć - należy zawsze utylizować urządzenia odczytującego / zapisującego, a najlepiej z usingoświadczeniem. (W rzeczywistości żadna z tych klas nie ma finalizatorów ani nie powinny.)

Osobiście wolę mieć również instrukcję using dla strumienia. Możesz usingzgrabnie zagnieżdżać instrukcje bez nawiasów klamrowych:

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

Mimo że usinginstrukcja dla strumienia jest nieco zbędna (chyba, że StreamReaderkonstruktor zgłosi wyjątek), uważam to za najlepszą praktykę, ponieważ wtedy, jeśli pozbędziesz się StreamReaderi po prostu użyjesz strumienia bezpośrednio w późniejszym terminie, będziesz już mieć odpowiednią utylizację semantyka.


2
och dobrze, dzieje się tak tylko podczas wywoływania Dispose, a nie podczas rzekomego finalizowania.
— Nefzen

1
@Nefzen: To dlatego, że nie ma gwarancji, jakie zamówienie zostanie sfinalizowane. Jeśli zarówno StreamReader, jak i źródłowy Stream kwalifikują się do finalizacji, GC może najpierw sfinalizować strumień - wtedy Streamreader nie miałby odwołania do streamu. Z tego powodu można zwolnić niezarządzane zasoby tylko w ramach finalizacji (na przykład FileStream zamyka uchwyt pliku systemu Windows w finalizacji). Aha, i oczywiście, jeśli nigdy nie wyrzucisz, strumień zostanie ostatecznie zebrany (a plik zamknięty). To po prostu bardzo zła praktyka, aby nie pozbywać się strumienia.
— JMarsch

13
To zagnieżdżenie powoduje, że analizator kodu VS narzeka: 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.czy należy to po prostu zignorować? Jak dotąd nie miałem żadnych wyjątków ...
— HB

15
@HB: W tym przypadku można to bezpiecznie zignorować. Lub możesz po prostu utworzyć strumień w wywołaniu konstruktora StreamReader. Ostrzeżenie wygląda na fałszywe, biorąc pod uwagę, że dokumentacja IDisposable.Disposejawnie stwierdza: "Jeśli metoda Dispose obiektu jest wywoływana więcej niż raz, obiekt musi ignorować wszystkie wywołania po pierwszym. Obiekt nie może zgłaszać wyjątku, jeśli jego metoda Dispose to dzwoniono wiele razy. ”
— Jon Skeet

5
@JonSkeet: Właściwie jest taka strona , miałeś rację, to jest fałszywe :)
— HB

45

To jest stara, ale chciałem zrobić dziś coś podobnego i stwierdziłem, że wszystko się zmieniło. Od .net 4.5 istnieje leaveOpenargument:

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

Jedynym problemem jest to, że nie jest do końca oczywiste, co ustawić dla innych parametrów. Oto pomoc:

Ze strony msdn dla StreamReader Constructor (Stream):

Ten konstruktor inicjuje kodowanie do UTF8Encoding, właściwość BaseStream przy użyciu parametru stream i wewnętrzny rozmiar buforu do 1024 bajtów.

Że właśnie liście detectEncodingFromByteOrderMarks, który sądząc przez kod źródłowy jesttrue

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

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

Byłoby miło, gdyby niektóre z tych wartości domyślnych zostały ujawnione lub gdyby argumenty były opcjonalne, abyśmy mogli po prostu określić te, które chcemy.


Bardzo fajne informacje! Nigdy nie słyszałem o tym nowym parametrze i faktycznie ma to duży sens.
— julealgon

3
Dla leniwych ludzi takich jak ja, krótką odpowiedzią na pozostawienie otwartego strumienia byłoby:using (var streamReader = new StreamReader(myStream, Encoding.UTF8, true, 1024, true))
— beawolf

29

Tak. Możesz to sprawdzić, patrząc na implementację za pomocą Reflectora.

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

Sześć lat później, ale może to komuś pomoże.

StreamReader zamyka połączenie po jego usunięciu. Jednak „using (Stream stream = ...) {...}” with StreamReader / StreamWriter może spowodować dwukrotne usunięcie Stream: (1) po usunięciu obiektu StreamReader (2) i gdy Stream using block zamyka się. Powoduje to ostrzeżenie CA2202 podczas uruchamiania analizy kodu VS.

Innym rozwiązaniem, zaczerpniętym bezpośrednio ze strony CA2202 , jest użycie bloku try / last . Skonfiguruj poprawnie, spowoduje to tylko jednokrotne zamknięcie połączenia.

W dolnej części CA2202 firma Microsoft zaleca użycie następujących elementów:

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();
}

zamiast...

// Generates a CA2202 warning
using (Stream stream = new FileStream("file.txt", FileMode.Open))
using (XmlReader reader = new XmlReader (stream))
{
    // Use the reader object...
}

2
Ostrzeżenie jest również omawiane w komentarzach do zaakceptowanej odpowiedzi. Jon Skeet oferuje tam porady.
— Marcin

Należy również pamiętać, że instrukcja using jest w rzeczywistości konwertowana przez kompilator na blok try-last.
— Jason Kelley

2

Tak. Wywołanie Dispose () on i IDisposable (co „użycie” robi) powinno spowodować, że obiekt wyczyści wszystkie swoje zasoby. Obejmuje to opróżnianie strumieni i zamykanie ich deskryptorów plików.

Jeśli w twoim przypadku chcesz przekazać to innym metodom, musisz upewnić się, że te metody nie wykonują odczytu / zapisu w bloku using.



-2

strumień jest usuwany za pomocą słowa kluczowego „using” lub jawnie wywołując dispose

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.