Apakah membuang streamreader menutup arus?

166

Saya mengirim aliran ke metode untuk menulis, dan dalam metode itu saya menggunakan pembaca biner / wrtier. Ketika pembaca / penulis dibuang, baik oleh usingatau hanya ketika itu tidak direferensikan, apakah alirannya ditutup juga ??

Saya akan mengirim BinaryReader / Writer, tetapi saya menggunakan StreamReader juga (mungkin saya harus berkeliling. Saya hanya menggunakannya untuk GetLine dan ReadLine). Ini cukup merepotkan jika menutup aliran setiap kali penulis / pembaca ditutup.

Nefzen
sumber

Jawaban:

204

Ya, StreamReader, StreamWriter, BinaryReaderdan BinaryWritersemua dekat / membuang aliran yang mendasari mereka ketika Anda menelepon Disposemereka. Mereka tidak membuang arus jika pembaca / penulis hanya mengumpulkan sampah - Anda harus selalu membuang pembaca / penulis, lebih disukai dengan usingpernyataan. (Faktanya, tidak ada satu pun dari kelas-kelas ini yang memiliki finalizer, dan mereka juga seharusnya tidak.)

Secara pribadi saya lebih suka memiliki pernyataan menggunakan untuk aliran juga. Anda dapat membuat usingpernyataan tanpa kawat gigi dengan rapi:

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

Meskipun usingpernyataan untuk aliran agak berlebihan (kecuali jika StreamReaderkonstruktor melempar pengecualian) Saya menganggapnya sebagai praktik terbaik saat itu jika Anda menyingkirkan StreamReaderdan hanya menggunakan aliran langsung di kemudian hari, Anda sudah memiliki hak untuk membuang semantik.

Jon Skeet
sumber
2
oh bagus, itu hanya terjadi saat menelepon Buang, bukan saat seharusnya menyelesaikan.
Nefzen
1
@Nefzen: Itu karena tidak ada jaminan urutan objek Anda akan selesai. Jika StreamReader dan Stream yang mendasarinya memenuhi syarat untuk finalisasi, GC mungkin akan menyelesaikan stream terlebih dahulu - maka streamreader tidak akan memiliki referensi untuk streaming. Untuk alasan ini, Anda hanya dapat merilis sumber daya yang tidak dikelola di dalam penyelesaian (misalnya, FileStream menutup pegangan file windows-nya dalam penyelesaiannya). Oh, dan tentu saja, jika Anda tidak pernah membuang, aliran akan tetap dikumpulkan pada akhirnya (dan file ditutup). Ini hanya praktik yang sangat buruk untuk tidak membuang arus.
JMarsch
13
Sarang ini menyebabkan penganalisa kode VS mengeluh: 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.Haruskah itu diabaikan? Saya tidak mendapatkan pengecualian sejauh ini ...
HB
15
@HB: Aman untuk mengabaikannya dalam kasus ini. Atau Anda bisa membuat aliran di panggilan konstruktor ke StreamReader. Peringatan itu terlihat palsu bagi saya, mengingat bahwa dokumen untuk IDisposable.Disposemenyatakan secara eksplisit: "Jika metode Buang objek disebut lebih dari sekali, objek harus mengabaikan semua panggilan setelah yang pertama. Objek tidak boleh membuang pengecualian jika metode Buangnya adalah disebut beberapa kali. "
Jon Skeet
5
@ JonSkeet: Sebenarnya ada halaman untuk ini , Anda benar, ini palsu :)
HB
45

Ini adalah yang lama, tetapi saya ingin melakukan sesuatu yang serupa hari ini dan menemukan bahwa semuanya telah berubah. Sejak .net 4.5, ada leaveOpenargumen:

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

Satu-satunya masalah adalah bahwa tidak sepenuhnya jelas apa yang harus ditetapkan untuk parameter lainnya. Berikut ini beberapa bantuan:

Dari halaman msdn untuk StreamReader Constructor (Stream):

Konstruktor ini menginisialisasi pengodean ke UTF8Encoding, properti BaseStream menggunakan parameter stream, dan ukuran buffer internal hingga 1024 byte.

Itu hanya daun detectEncodingFromByteOrderMarksyang menilai oleh kode sumber adalahtrue

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

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

Akan lebih baik jika beberapa dari default itu diekspos atau jika argumennya opsional sehingga kita bisa menentukan yang kita inginkan.

acarlon
sumber
Info yang sangat bagus! Belum pernah mendengar tentang parameter baru ini dan itu benar-benar masuk akal.
julealgon
3
Untuk orang-orang malas seperti saya, jawaban singkat untuk membiarkan aliran terbuka adalah:using (var streamReader = new StreamReader(myStream, Encoding.UTF8, true, 1024, true))
beawolf
29

Ya, benar. Anda dapat memverifikasi ini dengan melihat implementasinya dengan 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);
        }
    }
}
Brian Rasmussen
sumber
13

Enam tahun terlambat tapi mungkin ini bisa membantu seseorang.

StreamReader tidak menutup koneksi ketika dibuang. Namun, "menggunakan (Stream stream = ...) {...}" dengan StreamReader / StreamWriter dapat mengakibatkan Stream dibuang dua kali: (1) ketika objek StreamReader dibuang (2) dan ketika blok Stream menggunakan menutup. Ini menghasilkan peringatan CA2202 ketika menjalankan analisis kode VS.

Solusi lain, diambil langsung dari halaman CA2202 , adalah dengan menggunakan blok coba / akhirnya. Pengaturan dengan benar, ini hanya akan menutup koneksi sekali.

Di dekat bagian bawah CA2202 , Microsoft merekomendasikan untuk menggunakan yang berikut:

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

dari pada...

// Generates a CA2202 warning
using (Stream stream = new FileStream("file.txt", FileMode.Open))
using (XmlReader reader = new XmlReader (stream))
{
    // Use the reader object...
}
Sunsetquest
sumber
2
Peringatan itu dibahas dalam komentar dari jawaban yang diterima juga. Jon Skeet menawarkan beberapa saran di sana.
Marcin
Perlu diketahui juga bahwa pernyataan penggunaan sebenarnya diubah menjadi blok coba-akhirnya oleh kompiler.
Jason Kelley
2

Iya. Memanggil Buang () pada dan IDisposable (yang "menggunakan" tidak) harus membuat objek membersihkan semua sumber dayanya. Ini termasuk stream flushing dan menutup deskriptor file mereka.

Jika, dalam kasus Anda, Anda ingin meneruskannya ke metode lain, maka Anda perlu memastikan bahwa metode tersebut tidak melakukan pembacaan / penulisan mereka dalam blok penggunaan.

Joe M
sumber
-2

aliran dibuang baik dengan "menggunakan" kata kunci atau panggilan buang secara eksplisit

Ahmed Said
sumber