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.

Nefzen
źródło

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.

Jon Skeet
źródło
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.

akarlon
źródło
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);
        }
    }
}
Brian Rasmussen
źródło
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...
}
Sunsetquest
źródło
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.

Joe M
źródło
-2

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

Ahmed Said
źródło