C # biarkan saya melakukan hal berikut (contoh dari MSDN):
using (Font font3 = new Font("Arial", 10.0f),
font4 = new Font("Arial", 10.0f))
{
// Use font3 and font4.
}
Apa yang terjadi jika font4 = new Font
melempar? Dari apa yang saya pahami font3 akan membocorkan sumber daya dan tidak akan dibuang.
- Apakah ini benar? (font4 tidak akan dibuang)
- Apakah ini berarti
using(... , ...)
harus dihindari sama sekali demi penggunaan bersarang?
c#
using
using-statement
Benjamin Gruenbaum
sumber
sumber
using(... , ...)
dikompilasi menjadi bersarang menggunakan blok, tetapi saya tidak tahu pasti.using
sama sekali, GC akan tetap mengumpulkannya.finally
blok, itu tidak akan memasuki blok sampai semua sumber daya dibangun.using
menjaditry
-finally
, ekspresi inisialisasi dievaluasi di luartry
. Jadi itu pertanyaan yang masuk akal.Jawaban:
Tidak.
Kompilator akan membuat
finally
blok terpisah untuk setiap variabel.The spek (§8.13) mengatakan:
sumber
PEMBARUAN : Saya menggunakan pertanyaan ini sebagai dasar untuk artikel yang dapat ditemukan di sini ; lihat untuk diskusi tambahan tentang masalah ini. Terima kasih atas pertanyaan yang bagus!
Meskipun jawaban Schabse tentu saja benar dan menjawab pertanyaan yang diajukan, ada varian penting dari pertanyaan Anda yang tidak Anda ajukan:
Biarkan saya membuatnya sedikit lebih jelas. Misalkan kita memiliki:
Sekarang kita punya
Ini sama dengan
BAIK. Misalkan
Whatever
melempar. Kemudianfinally
blok tersebut berjalan dan sumber daya dibatalkan alokasinya. Tidak masalah.Misalkan
Blah1()
melempar. Kemudian lemparan terjadi sebelum sumber daya dialokasikan. Objek telah dialokasikan tetapi ctor tidak pernah kembali, jadifoo
tidak pernah diisi. Kami tidak pernah masuktry
sehingga kami tidak pernah memasukkanfinally
keduanya. Referensi objek telah menjadi yatim piatu. Akhirnya GC akan menemukannya dan meletakkannya di antrean finalizer.handle
masih nol, jadi finalizer tidak melakukan apa pun. Perhatikan bahwa finalizer harus kuat dalam menghadapi objek yang sedang diselesaikan yang konstruktornya tidak pernah selesai . Anda dibutuhkan untuk menulis finalisator yang sekuat ini. Ini adalah alasan lain mengapa Anda harus menyerahkan tugas akhir penulisan kepada para ahli dan tidak mencoba melakukannya sendiri.Misalkan
Blah3()
melempar. Lemparan terjadi setelah sumber daya dialokasikan. Tapi sekali lagi,foo
tidak pernah diisi, kita tidak pernah masukfinally
, dan objek dibersihkan oleh thread finalizer. Kali ini pegangannya bukan nol, dan finalizer membersihkannya. Sekali lagi, finalizer berjalan pada objek yang konstruktornya tidak pernah berhasil, tetapi finalizer tetap berjalan. Jelas harus karena kali ini, ada pekerjaan yang harus dilakukan.Sekarang misalkan
Blah2()
lemparan. Lemparan terjadi setelah sumber daya dialokasikan tetapi sebelumhandle
diisi! Sekali lagi, finalizer akan berjalan tetapi sekaranghandle
masih nol dan kami membocorkan pegangannya!Anda perlu menulis kode yang sangat pintar untuk mencegah kebocoran ini terjadi. Sekarang, dalam kasus
Font
sumber daya Anda , siapa yang peduli? Kami membocorkan pegangan font, masalah besar. Tetapi jika Anda benar-benar positif memerlukan bahwa setiap sumber daya unmanaged dibersihkan tidak peduli apa waktu pengecualian adalah maka Anda memiliki masalah yang sangat sulit di tangan Anda.CLR harus menyelesaikan masalah ini dengan kunci. Sejak C # 4, kunci yang menggunakan
lock
pernyataan tersebut telah diimplementasikan seperti ini:Enter
telah ditulis dengan sangat hati-hati sehingga apa pun pengecualian yang dilemparkan ,lockEntered
disetel ke true jika dan hanya jika kunci benar-benar diambil. Jika Anda memiliki persyaratan serupa maka yang perlu Anda lakukan sebenarnya adalah menulis:dan tulis
AllocateResource
dengan cerdikMonitor.Enter
sehingga apa pun yang terjadi di dalamnyaAllocateResource
,handle
isinya jika dan hanya jika perlu dibatalkan alokasinya.Menjelaskan teknik untuk melakukannya berada di luar cakupan jawaban ini. Konsultasikan dengan ahlinya jika Anda memiliki persyaratan ini.
sumber
Blah
panggilan metode tersebut. Apa yang menghentikan ThreadAbortException terjadi di salah satu titik tersebut?AllocateResource
tetapi sebelum penugasan kex
. AThreadAbortException
bisa terjadi pada saat itu. Semua orang di sini tampaknya kehilangan maksud saya, yaitu pembuatan sumber daya dan penugasan referensi ke variabel bukanlah operasi atom . Untuk memecahkan masalah yang saya identifikasi, Anda harus menjadikannya operasi atom.Sebagai pelengkap jawaban @SLaks, berikut IL untuk kode Anda:
Perhatikan blok percobaan / akhirnya yang bersarang.
sumber
Kode ini (berdasarkan contoh asli):
Ini menghasilkan CIL berikut ini (dalam Visual Studio 2013 , menargetkan .NET 4.5.1):
Seperti yang Anda lihat,
try {}
pemblokiran tidak dimulai sampai setelah alokasi pertama, yang berlangsung diIL_0012
. Sekilas, ini tampaknya mengalokasikan item pertama dalam kode yang tidak dilindungi. Namun, perhatikan bahwa hasilnya disimpan di lokasi 0. Jika alokasi kedua kemudian gagal, blok luarfinally {}
dijalankan, dan ini mengambil objek dari lokasi 0, yaitu alokasi pertamafont3
, dan memanggilDispose()
metodenya.Menariknya, menguraikan rakitan ini dengan dotPeek menghasilkan sumber yang disusun kembali berikut ini:
Kode yang didekompilasi menegaskan bahwa semuanya benar dan pada
using
dasarnya diperluas menjadiusing
s bersarang . Kode CIL agak membingungkan untuk dilihat, dan saya harus menatapnya selama beberapa menit sebelum saya benar-benar memahami apa yang terjadi, jadi saya tidak terkejut bahwa beberapa 'dongeng istri lama' mulai bermunculan. ini. Namun, kode yang dihasilkan adalah kebenaran yang tidak dapat disangkal.sumber
Berikut adalah contoh kode untuk membuktikan jawaban @SLaks:
sumber
font4 = new Font
melempar? Dari apa yang saya pahami font3 akan membocorkan sumber daya dan tidak akan dibuang."