C # 2008
Saya telah mengerjakan ini untuk sementara waktu sekarang, dan saya masih bingung tentang penggunaan metode finalisasi dan buang dalam kode. Pertanyaan saya di bawah ini:
Saya tahu bahwa kita hanya perlu penyelesai sementara membuang sumber daya yang tidak dikelola. Namun, jika ada sumber daya yang dikelola yang membuat panggilan ke sumber daya yang tidak dikelola, apakah masih perlu menerapkan finalizer?
Namun, jika saya mengembangkan kelas yang tidak menggunakan sumber daya yang tidak dikelola - langsung atau tidak langsung, haruskah saya mengimplementasikannya
IDisposable
agar klien dari kelas itu dapat menggunakan 'pernyataan penggunaan'?Apakah layak menerapkan IDisposable hanya untuk memungkinkan klien kelas Anda menggunakan pernyataan menggunakan?
using(myClass objClass = new myClass()) { // Do stuff here }
Saya telah mengembangkan kode sederhana di bawah ini untuk menunjukkan penyelesaian / buang penggunaan:
public class NoGateway : IDisposable { private WebClient wc = null; public NoGateway() { wc = new WebClient(); wc.DownloadStringCompleted += wc_DownloadStringCompleted; } // Start the Async call to find if NoGateway is true or false public void NoGatewayStatus() { // Start the Async's download // Do other work here wc.DownloadStringAsync(new Uri(www.xxxx.xxx)); } private void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { // Do work here } // Dispose of the NoGateway object public void Dispose() { wc.DownloadStringCompleted -= wc_DownloadStringCompleted; wc.Dispose(); GC.SuppressFinalize(this); } }
Pertanyaan tentang kode sumber:
Di sini saya belum menambahkan finalizer, dan biasanya finalizer akan dipanggil oleh GC, dan finalizer akan memanggil Buang. Karena saya tidak memiliki finalizer, kapan saya memanggil metode Buang? Apakah klien dari kelas yang harus memanggilnya?
Jadi kelas saya dalam contoh disebut NoGateway dan klien dapat menggunakan dan membuang kelas seperti ini:
using(NoGateway objNoGateway = new NoGateway()) { // Do stuff here }
Apakah metode Buang dipanggil secara otomatis ketika eksekusi mencapai akhir blok penggunaan, atau apakah klien harus memanggil metode buang secara manual? yaitu
NoGateway objNoGateway = new NoGateway(); // Do stuff with object objNoGateway.Dispose(); // finished with it
Saya menggunakan
WebClient
kelas diNoGateway
kelas saya . KarenaWebClient
mengimplementasikanIDisposable
antarmuka, apakah ini berartiWebClient
secara tidak langsung menggunakan sumber daya yang tidak dikelola? Apakah ada aturan yang keras dan cepat untuk mengikuti ini? Bagaimana saya tahu bahwa suatu kelas menggunakan sumber daya yang tidak dikelola?
sumber
Jawaban:
Pola IDisposable yang direkomendasikan ada di sini . Saat memprogram kelas yang menggunakan IDisposable, umumnya Anda harus menggunakan dua pola:
Saat menerapkan kelas tertutup yang tidak menggunakan sumber daya yang tidak dikelola, Anda cukup menerapkan metode Buang seperti dengan implementasi antarmuka normal:
Saat menerapkan kelas yang tidak disegel, lakukan seperti ini:
Perhatikan bahwa saya belum menyatakan finalizer di
B
; Anda hanya harus menerapkan finalizer jika Anda memiliki sumber daya yang tidak terkelola untuk dibuang. CLR berurusan dengan objek yang dapat diselesaikan secara berbeda dengan objek yang tidak dapat diselesaikan, meskipunSuppressFinalize
disebut.Jadi, Anda tidak boleh mendeklarasikan finalizer kecuali Anda harus melakukannya, tetapi Anda memberikan hook kelas kepada Anda untuk menelepon Anda
Dispose
dan mengimplementasikan finalizer sendiri jika mereka menggunakan sumber daya yang tidak dikelola secara langsung:Jika Anda tidak menggunakan sumber daya yang tidak dikelola secara langsung (
SafeHandle
dan teman-teman tidak masuk hitungan, karena mereka menyatakan finalizer mereka sendiri), maka jangan mengimplementasikan finalizer, karena GC berurusan dengan kelas finalizable secara berbeda, bahkan jika Anda kemudian menekan finalizer. Perhatikan juga, meskipunB
tidak memiliki finalizer, ia tetap memanggilSuppressFinalize
untuk berurusan dengan benar dengan setiap subclass yang mengimplementasikan finalizer.Ketika sebuah kelas mengimplementasikan antarmuka IDisposable, itu berarti bahwa di suatu tempat ada beberapa sumber daya yang tidak dikelola yang harus dihilangkan ketika Anda selesai menggunakan kelas. Sumber daya aktual dirangkum dalam kelas; Anda tidak perlu menghapusnya secara eksplisit. Cukup dengan menelepon
Dispose()
atau membungkus kelas dalam ausing(...) {}
akan memastikan sumber daya yang tidak dikelola dihilangkan seperlunya.sumber
IDisposable
, kemungkinan itu akan bertahan sebentar. Anda menyimpan CLR dengan upaya menyalinnya dari Gen0 -> Gen1 -> Gen2Pola resmi untuk mengimplementasikannya
IDisposable
sulit dipahami. Saya percaya ini lebih baik :Sebuah lebih baik solusi adalah memiliki aturan bahwa Anda selalu harus membuat kelas wrapper untuk setiap sumber daya unmanaged yang Anda butuhkan untuk menangani:
Dengan
SafeHandle
dan turunannya, kelas-kelas ini harus sangat jarang .Hasil untuk kelas sekali pakai yang tidak berurusan langsung dengan sumber daya yang tidak dikelola, bahkan di hadapan warisan, sangat kuat: mereka tidak perlu lagi peduli dengan sumber daya yang tidak dikelola lagi . Mereka akan mudah diimplementasikan dan dipahami:
sumber
disposed
bendera dan memeriksa yang sesuai.disposed
bendera, periksa sebelum membuang dan atur setelah membuang. Lihat di sini untuk idenya. Anda juga harus memeriksa tanda sebelum metode apa pun di kelas. Masuk akal? Apakah ini rumit?Perhatikan bahwa implementasi IDisposable harus mengikuti pola di bawah ini (IMHO). Saya mengembangkan pola ini berdasarkan info dari beberapa .NET "allah" yang bagus .NET Framework Design Guidelines (perhatikan bahwa MSDN tidak mengikuti ini karena alasan tertentu!). Pedoman .NET Framework Design ditulis oleh Krzysztof Cwalina (CLR Architect pada saat itu) dan Brad Abrams (saya percaya Manajer Program CLR pada saat itu) dan Bill Wagner ([C Efektif] dan [Lebih Efektif C #] (cukup ambil lihat ini di Amazon.com:
Perhatikan bahwa Anda tidak akan pernah mengimplementasikan Finalizer kecuali kelas Anda secara langsung berisi (bukan warisan) sumber daya yang tidak dikelola. Setelah Anda menerapkan Finalizer di kelas, bahkan jika itu tidak pernah dipanggil, dijamin akan hidup untuk koleksi tambahan. Itu secara otomatis ditempatkan pada Antrian Finalisasi (yang berjalan pada satu utas). Juga, satu catatan yang sangat penting ... semua kode yang dieksekusi dalam Finalizer (harus Anda perlu menerapkannya) HARUS aman dari thread DAN pengecualian aman! Hal-hal buruk akan terjadi sebaliknya ... (yaitu perilaku yang tidak ditentukan dan dalam kasus pengecualian, aplikasi crash fatal yang tidak dapat dipulihkan).
Pola yang saya kumpulkan (dan menulis cuplikan kode) berikut:
Berikut adalah kode untuk mengimplementasikan IDisposable di kelas turunan. Perhatikan bahwa Anda tidak perlu membuat daftar warisan secara eksplisit dari IDisposable dalam definisi kelas turunan.
Saya telah memposting implementasi ini di blog saya di: Cara Menerapkan Pola Buang dengan Benar
sumber
Interlocked.Exchange
integerIsDisposed
di fungsi pembungkus non-virtual akan lebih aman.Saya setuju dengan PM100 (dan seharusnya secara eksplisit mengatakan ini di posting saya sebelumnya).
Anda tidak boleh menerapkan IDisposable di kelas kecuali Anda membutuhkannya. Untuk lebih spesifik, ada sekitar 5 kali ketika Anda membutuhkan / harus menerapkan IDisposable:
Kelas Anda secara eksplisit berisi (yaitu bukan melalui warisan) sumber daya yang dikelola yang menerapkan IDisposable dan harus dibersihkan setelah kelas Anda tidak lagi digunakan. Misalnya, jika kelas Anda berisi turunan dari Stream, DbCommand, DataTable, dll.
Kelas Anda secara eksplisit berisi sumber daya yang dikelola yang menerapkan metode Tutup () - misalnya IDataReader, IDbConnection, dll. Perhatikan bahwa beberapa kelas ini menerapkan IDisposable dengan memiliki metode Buang () serta Tutup ().
Kelas Anda secara eksplisit berisi sumber daya yang tidak dikelola - misalnya objek COM, pointer (ya, Anda dapat menggunakan pointer dalam C # yang dikelola tetapi mereka harus dideklarasikan dalam blok 'tidak aman', dll. Dalam kasus sumber daya yang tidak dikelola, Anda juga harus memastikan untuk sebut System.Runtime.InteropServices.Marshal.ReleaseComObject () pada RCW. Meskipun RCW, secara teori, merupakan pembungkus yang dikelola, masih ada penghitungan referensi yang terjadi di bawah selimut.
Jika kelas Anda berlangganan acara menggunakan referensi kuat. Anda harus membatalkan registrasi / melepaskan diri dari peristiwa tersebut. Selalu pastikan ini bukan nol dulu sebelum mencoba membatalkan pendaftaran / lepaskan mereka !.
Kelas Anda mengandung kombinasi apa pun di atas ...
Alternatif yang disarankan untuk bekerja dengan objek COM dan harus menggunakan Marshal.ReleaseComObject () adalah dengan menggunakan kelas System.Runtime.InteropServices.SafeHandle.
BCL (Tim Perpustakaan Kelas Dasar) memiliki posting blog yang bagus tentang hal ini di sini http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx
Satu catatan yang sangat penting untuk dibuat adalah bahwa jika Anda bekerja dengan WCF dan membersihkan sumber daya, Anda harus HAMPIR SELALU menghindari blok 'menggunakan'. Ada banyak posting blog di luar sana dan beberapa di MSDN tentang mengapa ini adalah ide yang buruk. Saya juga mempostingnya di sini - Jangan gunakan 'using ()' dengan proxy WCF
sumber
Menggunakan lambdas bukan IDisposable.
Saya tidak pernah senang dengan keseluruhan menggunakan / ide IDisposable. Masalahnya adalah itu mengharuskan penelepon untuk:
Metode pilihan baru saya adalah menggunakan metode pabrik dan lambda sebagai gantinya
Bayangkan saya ingin melakukan sesuatu dengan SqlConnection (sesuatu yang harus dibungkus menggunakan). Klasik yang akan Anda lakukan
Jalan baru
Dalam kasus pertama penelepon tidak bisa menggunakan sintaks menggunakan. Dalam kasus kedua, pengguna tidak memiliki pilihan. Tidak ada metode yang membuat objek SqlConnection, pemanggil harus memanggil DoWithConnection.
DoWithConnection terlihat seperti ini
MakeConnection
sekarang pribadisumber
DoForAll(Action<T>) where T:IComparable<T>
, memanggil delegasi yang ditunjukkan pada setiap catatan. Diberikan dua objek seperti itu, yang keduanya akan mengembalikan data dalam urutan, bagaimana satu output semua item yang ada dalam satu koleksi tetapi tidak yang lain? Jika tipe diimplementasikanIEnumerable<T>
, seseorang dapat melakukan operasi penggabungan, tetapi itu tidak akan berhasilDoForAll
.DoForAll
koleksi tanpa harus menyalin satu, secara keseluruhan, ke dalam beberapa struktur lain, adalah dengan menggunakan dua utas, yang akan menjadi lebih hoggish sumber daya daripada hanya menggunakan pasangan IEnumerable dan berhati-hati untuk membebaskan mereka.tidak ada yang menjawab pertanyaan tentang apakah Anda harus menerapkan IDisposable meskipun Anda tidak membutuhkannya.
Jawaban singkat: Tidak
Jawaban panjang:
Ini akan memungkinkan konsumen kelas Anda untuk menggunakan 'menggunakan'. Pertanyaan yang akan saya tanyakan adalah - mengapa mereka melakukannya? Sebagian besar devs tidak akan menggunakan 'menggunakan' kecuali mereka tahu bahwa mereka harus - dan bagaimana mereka tahu. Antara
Jadi dengan mengimplementasikan IDisposable Anda memberitahu devs (setidaknya beberapa) bahwa kelas ini merangkum sesuatu yang harus dirilis. Mereka akan menggunakan 'menggunakan' - tetapi ada kasus-kasus lain di mana penggunaan tidak memungkinkan (ruang lingkup objek tidak lokal); dan mereka harus mulai khawatir tentang masa hidup benda-benda dalam kasus-kasus lain - saya pasti khawatir. Tetapi ini tidak perlu
Anda menerapkan Idisposable untuk memungkinkan mereka menggunakan menggunakan, tetapi mereka tidak akan menggunakan menggunakan kecuali jika Anda memberi tahu mereka.
Jadi jangan lakukan itu
sumber
Jika Anda menggunakan objek terkelola lainnya yang menggunakan sumber daya yang tidak dikelola, itu bukan tanggung jawab Anda untuk memastikan mereka selesai. Tanggung jawab Anda adalah memanggil Buanglah benda-benda itu saat Buang dipanggil pada benda Anda, dan benda itu berhenti di sana.
Jika kelas Anda tidak menggunakan sumber daya yang langka, saya gagal melihat mengapa Anda membuat kelas Anda mengimplementasikan IDisposable. Anda hanya harus melakukannya jika Anda:
Ya, kode yang menggunakan kode Anda harus memanggil metode Buang objek Anda. Dan ya, kode yang menggunakan objek Anda dapat digunakan
using
seperti yang Anda tunjukkan.(2 lagi?) Sepertinya WebClient menggunakan sumber daya yang tidak dikelola, atau sumber daya yang dikelola lainnya yang menerapkan IDisposable. Namun, alasan pastinya tidak penting. Yang penting adalah mengimplementasikan IDisposable, sehingga Anda harus bertindak berdasarkan pengetahuan itu dengan membuang objek ketika Anda selesai menggunakannya, bahkan jika ternyata WebClient tidak menggunakan sumber daya lain sama sekali.
sumber
Buang pola:
Contoh warisan:
sumber
Beberapa aspek dari jawaban lain sedikit salah karena 2 alasan:
Pertama,
sebenarnya setara dengan:
Ini mungkin terdengar konyol karena operator 'baru' tidak boleh mengembalikan 'null' kecuali Anda memiliki pengecualian OutOfMemory. Tetapi pertimbangkan kasus-kasus berikut: 1. Anda memanggil FactoryClass yang mengembalikan sumber daya IDisposable atau 2. Jika Anda memiliki tipe yang mungkin atau mungkin tidak mewarisi dari IDisposable tergantung pada implementasinya - ingat bahwa saya telah melihat pola IDisposable diimplementasikan secara salah banyak kali di banyak klien di mana pengembang hanya menambahkan metode Buang () tanpa mewarisi dari IDisposable (buruk, buruk, buruk). Anda juga dapat memiliki kasus sumber daya IDisposable yang dikembalikan dari properti atau metode (lagi buruk, buruk, buruk - jangan 'memberikan sumber daya IDisposable Anda)
Jika operator 'as' mengembalikan null (atau properti atau metode yang mengembalikan sumber daya), dan kode Anda di blok 'using' melindungi terhadap 'null', kode Anda tidak akan meledak ketika mencoba memanggil Buang di objek nol karena cek nol 'bawaan'.
Alasan kedua balasan Anda tidak akurat adalah karena stmt berikut:
Pertama, Finalisasi (dan juga GC itu sendiri) adalah non-deterministik. CLR menentukan kapan akan memanggil finalizer. yaitu pengembang / kode tidak tahu. Jika pola IDisposable diimplementasikan dengan benar (seperti yang saya posting di atas) dan GC.SuppressFinalize () telah dipanggil, Finalizer TIDAK akan dipanggil. Ini adalah salah satu alasan utama untuk menerapkan pola dengan benar. Karena hanya ada 1 utas Finalizer per proses yang dikelola, terlepas dari jumlah prosesor logis, Anda dapat dengan mudah menurunkan kinerja dengan mencadangkan atau bahkan menggantung utas Finalizer dengan lupa menelepon GC.SuppressFinalize ().
Saya telah memposting implementasi Pola Buang yang benar di blog saya: Cara Menerapkan Pola Buang dengan Benar
sumber
NoGateway = new NoGateway();
danNoGateway != null
?1) WebClient adalah tipe yang dikelola, jadi Anda tidak perlu finalizer. Finalizer diperlukan jika pengguna Anda tidak membuang () kelas NoGateway Anda dan tipe asli (yang tidak dikumpulkan oleh GC) perlu dibersihkan setelahnya. Dalam hal ini, jika pengguna tidak memanggil Buang (), WebClient yang terkandung akan dibuang oleh GC tepat setelah NoGateway melakukannya.
2) Secara tidak langsung ya, tetapi Anda tidak perlu khawatir tentang hal itu. Kode Anda benar sebagai tegakan dan Anda tidak dapat mencegah pengguna Anda lupa untuk Membuang () dengan sangat mudah.
sumber
Pola dari msdn
sumber
setara dengan
Seorang finalizer dipanggil pada GC yang menghancurkan objek Anda. Ini bisa pada waktu yang sama sekali berbeda dari ketika Anda meninggalkan metode Anda. Buang IDisposable dipanggil segera setelah Anda meninggalkan blok menggunakan. Karenanya polanya biasanya digunakan untuk membebaskan sumber daya segera setelah Anda tidak membutuhkannya lagi.
sumber
Dari apa yang saya tahu, sangat disarankan untuk TIDAK menggunakan Finalizer / Destructor:
Sebagian besar, ini karena tidak tahu kapan atau JIKA itu akan dipanggil. Metode buang jauh lebih baik, terutama jika Anda menggunakan atau membuang langsung.
menggunakan itu baik. Gunakan :)
sumber