Apa yang terjadi jika saya kembali sebelum akhir menggunakan pernyataan? Akankah pembuangan dipanggil?

115

Saya memiliki kode berikut

using(MemoryStream ms = new MemoryStream())
{
     //code
     return 0;
}

The dispose()metode ini disebut pada akhir usingpernyataan kawat gigi }yang benar? Karena I returnsebelum akhir usingpernyataan, apakah MemoryStreambenda tersebut akan dibuang dengan benar? Apa yang terjadi di sini?

NLV
sumber
4
@JonH: Temukan duplikat persisnya, lalu pilih untuk menutup dalam kasus itu.
Noldorin
@Noldorin: Saya mencari penipu dalam hal ini, karena saya pikir itu pasti sudah ditanyakan sebelumnya, tetapi saya tidak dapat menemukannya. Saya kira masih ada pertanyaan mudah di luar sana. :)
Randolpho
@JonH dan @Noldorin - duplikat akan disajikan saat pertanyaan dibuat, ia mencari "pertanyaan serupa", fitur yang tampaknya tidak cukup digunakan orang.
Adam Houldsworth
@Adam: coba sendiri. Salin / tempel judul dan lihat duplikat apa yang disajikan oleh sistem. Saya akan memberi Anda petunjuk: jawabannya tidak ada. Ditto jika Anda mencari melalui pencarian Google atau SO. Tampaknya pertanyaan ini belum pernah ditanyakan sebelumnya.
Randolpho
Aaap ... Aku ambil kembali. Saya baru saja menemukan duplikat yang mendekati, setelah beberapa pencarian yang sangat berdedikasi: stackoverflow.com/questions/2641692/… Sekarang, pertanyaannya ditanyakan sepenuhnya berbeda, tetapi pertanyaan utamanya hampir sama. Saya kira kita bisa menganggap ini sebagai penipuan.
Randolpho

Jawaban:

167

Ya, Disposeakan dipanggil. Ini dipanggil segera setelah eksekusi meninggalkan ruang lingkup usingblok, apa pun cara yang diperlukan untuk meninggalkan blok, baik itu akhir eksekusi blok, returnpernyataan, atau pengecualian.

Seperti yang ditunjukkan dengan benar oleh @Noldorin, menggunakan usingblok dalam kode dikompilasi menjadi try/ finally, dengan Disposedipanggil di finallyblok tersebut. Misalnya kode berikut:

using(MemoryStream ms = new MemoryStream())
{
     //code
     return 0;
}

efektif menjadi:

MemoryStream ms = new MemoryStream();
try
{
    // code
    return 0;
}
finally
{
    ms.Dispose();
}

Jadi, karena finallydijamin akan dieksekusi setelah tryblok selesai dieksekusi, terlepas dari jalur eksekusinya, Disposedijamin akan dipanggil, apa pun yang terjadi.

Untuk informasi selengkapnya, lihat artikel MSDN ini .

Tambahan:
Sedikit peringatan untuk ditambahkan: karena Disposedijamin akan dipanggil, adalah ide bagus untuk memastikan bahwa Disposetidak pernah ada pengecualian saat Anda menerapkan IDisposable. Sayangnya, ada beberapa kelas di pustaka inti yang melakukannya dalam keadaan tertentu ketika Disposedipanggil - Saya melihat Anda, Referensi Layanan WCF / Proksi Klien! - dan ketika itu terjadi, akan sangat sulit untuk melacak pengecualian asli jika Disposedipanggil selama pengecualian tumpukan rileks, karena pengecualian asli ditelan demi pengecualian baru yang dihasilkan oleh Disposepanggilan tersebut. Ini bisa sangat membuat frustrasi. Atau apakah itu membuat frustrasi? Salah satu dari keduanya. Mungkin keduanya.

Randolpho
sumber
4
Saya pikir Anda akan menemukannya secara efektif dikompilasi menjadi blok coba-akhirnya dengan panggilan ke Disposeakhirnya, jadi ini secara efektif bekerja dari implementasi finally, seperti yang Anda gambarkan.
Noldorin
@Noldorin: tepatnya. Meskipun saya kira saya bisa secara eksplisit tentang itu. Sunting akan datang ....
Randolpho
1
Perhatikan juga bahwa ada beberapa keadaan di mana blok akhirnya tidak dijamin untuk dijalankan, seperti menggunakan Environment.FailFast dan jika StackOverFlowException terjadi.
Christopher McAtackney
@ C.McAtackney: juga poin yang bagus. Juga, IIRC, OutOfMemoryException; pada dasarnya jika Anda tidak dapat menangkap pengecualian karena ini adalah kegagalan eksekusi kritis, Buang tidak akan dipanggil. Tentu saja, dalam kasus seperti itu program dijamin macet, bersama dengan memori apa pun yang dialokasikan untuknya, jadi dalam 99,9% kasus itu bukan masalah, kecuali Anda melakukan hal-hal miring seperti menulis ke file dalam metode pembuangan Anda . Selain kecelakaan program bencana, begitulah.
Randolpho
Anda tidak boleh menggunakan pernyataan 'using ()' dengan WCF - lihat artikel ini untuk info selengkapnya. Berikut potongan yang saya gunakan untuk proxy WCF: 'WCFProxy variableName = null; coba {variableName = new WCFProxy (); // kode TODO di sini variableName.Proxy.Close (); variableName.Dispose (); } menangkap (Pengecualian) {if (variableName! = null && variableName.Proxy! = null) {variableName.Proxy.Abort (); } lempar; } '
Dave Black
18

usingpernyataan berperilaku persis seperti try ... finallyblok, jadi akan selalu dijalankan di jalur keluar kode apa pun. Namun, saya yakin mereka tunduk pada situasi yang sangat sedikit dan jarang di mana finallyblok tidak dipanggil. Salah satu contoh yang dapat saya ingat adalah jika utas latar depan keluar sementara utas latar belakang aktif: semua utas selain GC dijeda, yang berarti finallyblok tidak berjalan.

Sunting jelas: mereka berperilaku sama selain logika yang memungkinkan mereka menangani objek IDisposable, d'oh.

Konten bonus: mereka dapat ditumpuk (di mana jenisnya berbeda):

using (SqlConnection conn = new SqlConnection("string"))
using (SqlCommand comm = new SqlCommand("", conn))
{

}

Dan juga dipisahkan dengan koma (dengan tipe yang sama):

using (SqlCommand comm = new SqlCommand("", conn), 
       SqlCommand comm2 = new SqlCommand("", conn))
{

}
Adam Houldsworth
sumber
4

Objek MemoryStream Anda akan dibuang dengan benar, tidak perlu khawatir tentang itu.

Otávio Décio
sumber
0

Lihatlah kode Anda di reflektor setelah Anda menyusunnya. Anda akan menemukan bahwa kompilator mem-refactors kode untuk memastikan bahwa pembuangan dipanggil di aliran.

Wil P.
sumber