Haruskah saya memanggil Tutup () atau Buang () untuk objek streaming?

151

Kelas seperti Stream, StreamReader, StreamWriterdll mengimplementasikan IDisposableinterface. Itu berarti, kita dapat memanggil Dispose()metode pada objek dari kelas-kelas ini. Mereka juga mendefinisikan publicmetode yang disebut Close(). Sekarang membingungkan saya, seperti apa yang harus saya panggil setelah saya selesai dengan benda? Bagaimana jika saya memanggil keduanya?

Kode saya saat ini adalah:

using (Stream responseStream = response.GetResponseStream())
{
   using (StreamReader reader = new StreamReader(responseStream))
   {
      using (StreamWriter writer = new StreamWriter(filename))
      {
         int chunkSize = 1024;
         while (!reader.EndOfStream)
         {
            char[] buffer = new char[chunkSize];
            int count = reader.Read(buffer, 0, chunkSize);
            if (count != 0)
            {
               writer.Write(buffer, 0, count);
            }
         }
         writer.Close();
      }
      reader.Close();
   }
}

Seperti yang Anda lihat, saya telah menulis using()konstruk, yang secara otomatis memanggil Dispose()metode pada setiap objek. Tapi saya juga menyebut Close()metode. Apakah tepat?

Tolong sarankan saya praktik terbaik saat menggunakan objek aliran. :-)

Contoh MSDN tidak menggunakan using()konstruk, dan memanggil Close()metode:

Apakah itu baik?

Nawaz
sumber
Jika Anda menggunakan ReSharper, Anda dapat mendefinisikan ini sebagai "antipattern" dalam katalog derai. ReSharper akan menandai setiap penggunaan sebagai kesalahan / petunjuk / peringatan terkait dengan definisi Anda. Dimungkinkan juga untuk menentukan bagaimana ReSharper harus menerapkan QuickFix untuk kejadian seperti itu.
Thorsten Hans
3
Hanya sebuah tip: Anda dapat menggunakan pernyataan menggunakan seperti itu untuk beberapa itens sekali pakai: using (Stream responseStream = response.GetResponseStream ()) menggunakan (pembaca StreamReader = new StreamReader (responseStream)) menggunakan (penulis StreamWriter = penulis baru StreamWriter (nama file)) {//...Beberapa kode}
Latrova
Anda tidak perlu membuat pernyataan pernyataan menggunakan seperti itu, Anda dapat menumpuknya di atas satu sama lain dan memiliki satu set kurung. Pada posting lain, saya menyarankan edit untuk potongan kode yang seharusnya menggunakan pernyataan dengan teknik itu jika Anda ingin melihat dan memperbaiki "panah kode" Anda: stackoverflow.com/questions/5282999/…
Timothy Gonzalez
2
@ Suncat2000 Anda dapat memiliki banyak pernyataan yang menggunakan, tetapi tidak membuatnya dan menumpuknya. Saya tidak bermaksud sintaks seperti ini yang membatasi jenis: using (MemoryStream ms1 = new MemoryStream(), ms2 = new MemoryStream()) { }. Maksud saya seperti ini di mana Anda dapat mendefinisikan kembali tipe:using (MemoryStream ms = new MemoryStream()) using (FileStream fs = File.OpenRead("c:\\file.txt")) { }
Timothy Gonzalez

Jawaban:

101

Lompatan cepat ke Reflector.NET menunjukkan bahwa Close()metode aktif StreamWriteradalah:

public override void Close()
{
    this.Dispose(true);
    GC.SuppressFinalize(this);
}

Dan StreamReaderadalah:

public override void Close()
{
    this.Dispose(true);
}

The Dispose(bool disposing)menimpa di StreamReaderyaitu:

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;
            /* deleted for brevity */
            base.Dispose(disposing);
        }
    }
}

The StreamWriterMetode ini mirip.

Jadi, membaca kode jelas bahwa Anda dapat memanggil Close()& Dispose()pada streaming sesering mungkin dan dalam urutan apa pun. Itu tidak akan mengubah perilaku dengan cara apa pun.

Jadi turun ke apakah lebih mudah digunakan Dispose(), Close()dan / atau using ( ... ) { ... }.

Preferensi pribadi saya adalah yang using ( ... ) { ... }harus selalu digunakan bila memungkinkan karena membantu Anda untuk "tidak lari dengan gunting".

Tapi, meski ini membantu ketepatan, hal itu mengurangi keterbacaan. Di C # kita sudah memiliki banyak penutupan kurung kurawal jadi bagaimana kita tahu yang mana yang benar-benar melakukan penutupan di sungai?

Jadi saya pikir yang terbaik adalah melakukan ini:

using (var stream = ...)
{
    /* code */

    stream.Close();
}

Itu tidak mempengaruhi perilaku kode, tetapi itu membantu keterbacaan.

Enigmativitas
sumber
20
" Dalam C # kita sudah memiliki banyak penutupan kurung kurawal jadi bagaimana kita tahu yang mana yang benar-benar melakukan penutupan di sungai? " Saya tidak berpikir bahwa ini adalah masalah besar: Aliran ditutup "pada waktu yang tepat", yaitu, ketika variabel keluar dari ruang lingkup dan tidak lagi diperlukan.
Heinzi
110
Hmm, tidak, itu adalah "mengapa dia menutupnya dua kali ??" kecepatan benjolan saat membaca.
Hans Passant
57
Saya tidak setuju dengan Close()panggilan yang berlebihan itu . Jika seseorang yang kurang berpengalaman melihat kode dan tidak tahu tentang usingdia akan: 1) mencarinya dan belajar , atau 2) menambahkan Close()secara manual buta . Jika dia memilih 2), mungkin beberapa pengembang lain akan melihat berlebihan Close()dan bukannya "terkekeh", menginstruksikan pengembang yang kurang berpengalaman. Saya tidak mendukung menyulitkan pengembang yang belum berpengalaman, tetapi saya mendukung untuk mengubahnya menjadi pengembang yang berpengalaman.
R. Martinho Fernandes
14
Jika Anda menggunakan + Close () dan menghidupkan / menganalisis, Anda mendapatkan "peringatan: CA2202: Microsoft.Usage: Objek 'f' dapat dibuang lebih dari sekali dalam metode 'Foo (string)'. Untuk menghindari menghasilkan Sistem. ObjectDisposedException Anda tidak boleh memanggil Buang lebih dari satu kali pada objek .: Baris: 41 "Jadi sementara implementasi saat ini baik-baik saja dengan memanggil Tutup dan Buang, menurut dokumentasi dan / analisis, itu tidak ok dan mungkin berubah di versi mendatang. bersih.
marc40000
4
+1 untuk jawaban yang bagus. Hal lain yang perlu dipertimbangkan. Mengapa tidak menambahkan komentar setelah kurung kurawal seperti // Tutup atau seperti yang saya lakukan, menjadi pemula, saya menambahkan satu liner setelah kurung kurawal yang tidak jelas. seperti misalnya di kelas panjang saya akan menambahkan // End Namespace XXX setelah kurung kurawal akhir, dan // Akhiri Kelas YYY setelah kurung kurawal akhir kedua. Apakah ini bukan untuk apa komentar. Hanya penasaran. :) Sebagai pemula, saya melihat kode tersebut, mengapa saya datang ke sini. Saya memang mengajukan pertanyaan "Mengapa perlu untuk penutupan kedua". Saya merasa baris kode tambahan tidak menambah kejelasan. Maaf.
Francis Rodgers
51

Tidak, Anda tidak harus memanggil metode-metode itu secara manual. Pada akhir usingblok Dispose()metode ini disebut secara otomatis yang akan membebaskan sumber daya yang tidak dikelola (setidaknya untuk kelas .NET BCL standar seperti stream, pembaca / penulis, ...). Jadi, Anda juga bisa menulis kode seperti ini:

using (Stream responseStream = response.GetResponseStream())
    using (StreamReader reader = new StreamReader(responseStream))
        using (StreamWriter writer = new StreamWriter(filename))
        {
            int chunkSize = 1024;
            while (!reader.EndOfStream)
            {
                 char[] buffer = new char[chunkSize];
                 int count = reader.Read(buffer, 0, chunkSize);
                 if (count != 0)
                 {
                     writer.Write(buffer, 0, count);
                 }
            }
         }

The Close()metode panggilan Dispose().

Darin Dimitrov
sumber
1
Saya cukup yakin Anda tidak perlu menjadi usingyang pertama responseStreamkarena dibungkus oleh readeryang akan memastikan ditutup ketika pembaca dibuang. +1 nontheless
Isak Savo
Ini membingungkan ketika Anda mengatakan The Close method calls Dispose... dan di sisa posting Anda, Anda menyiratkan itu Dispose()akan menelepon Close(), saya tidak boleh menelepon yang terakhir secara manual. Apakah Anda mengatakan mereka saling memanggil?
Nawaz
@Nawaz, posting saya membingungkan. Metode Close cukup memanggil Buang. Dalam kasus Anda, Anda perlu Buang untuk membebaskan sumber daya yang tidak dikelola. Dengan membungkus kode Anda dalam menggunakan pernyataan, metode Buang disebut.
Darin Dimitrov
3
Jawaban yang mengerikan. Diasumsikan Anda dapat menggunakan usingblok. Saya menerapkan kelas yang menulis dari waktu ke waktu dan karena itu tidak bisa.
Jez
5
@Jez Kelas Anda kemudian harus mengimplementasikan antarmuka IDisposable, dan mungkin juga Tutup () jika tutup adalah terminologi standar di area tersebut , sehingga kelas yang menggunakan kelas Anda dapat menggunakan using(atau, sekali lagi, pilih Pola Buang).
Dorus
13

Dokumentasi mengatakan bahwa kedua metode ini setara:

StreamReader.Close : Implementasi Tutup ini memanggil metode Buang melewati nilai sebenarnya.

StreamWriter.Close : Implementasi Tutup ini memanggil metode Buang melewati nilai sebenarnya.

Stream.Close : Metode ini memanggil Buang, tentukan true untuk melepaskan semua sumber daya.

Jadi, keduanya sama-sama valid:

/* Option 1, implicitly calling Dispose */
using (StreamWriter writer = new StreamWriter(filename)) { 
   // do something
} 

/* Option 2, explicitly calling Close */
StreamWriter writer = new StreamWriter(filename)
try {
    // do something
}
finally {
    writer.Close();
}

Secara pribadi, saya akan tetap dengan opsi pertama, karena mengandung lebih sedikit "noise".

Heinzi
sumber
5

Pada banyak kelas yang mendukung keduanya Close()dan Dispose()metode, dua panggilan akan setara. Namun, pada beberapa kelas, dimungkinkan untuk membuka kembali objek yang telah ditutup. Beberapa kelas semacam itu dapat menjaga beberapa sumber daya tetap hidup setelah Tutup, untuk mengizinkan pembukaan kembali; yang lain mungkin tidak menjaga sumber daya apa pun tetap hidup Close(), tetapi mungkin menetapkan bendera Dispose()untuk secara eksplisit melarang pembukaan kembali.

Kontrak untuk IDisposable.Disposesecara eksplisit mensyaratkan bahwa memanggilnya pada suatu objek yang tidak akan pernah digunakan lagi akan paling tidak berbahaya, jadi saya akan merekomendasikan memanggil salah satu IDisposable.Disposeatau metode yang dipanggil Dispose()pada setiap IDisposableobjek, apakah seseorang juga memanggil atau tidak Close().

supercat
sumber
FYI inilah artikel di blog MSDN yang menjelaskan tentang Tutup dan Buang kesenangan. blogs.msdn.com/b/kimhamil/archive/2008/03/15/...
JamieSee
1

Ini adalah pertanyaan lama, tetapi sekarang Anda dapat menulis menggunakan pernyataan tanpa harus memblokir masing-masing. Mereka akan dibuang dalam urutan terbalik ketika blok yang mengandung selesai.

using var responseStream = response.GetResponseStream();
using var reader = new StreamReader(responseStream);
using var writer = new StreamWriter(filename);

int chunkSize = 1024;
while (!reader.EndOfStream)
{
    char[] buffer = new char[chunkSize];
    int count = reader.Read(buffer, 0, chunkSize);
    if (count != 0)
    {
        writer.Write(buffer, 0, count);
    }
}

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using

Todd Skelton
sumber