Yang lain sudah membahas perbedaan antara Disposedan Finalize(btw Finalizemetode ini masih disebut destructor dalam spesifikasi bahasa), jadi saya hanya akan menambahkan sedikit tentang skenario di mana Finalizemetode ini berguna.
Beberapa jenis merangkum sumber daya sekali pakai dengan cara yang mudah digunakan dan membuangnya dalam satu tindakan. Penggunaan umum sering seperti ini: buka, baca atau tulis, tutup (Buang). Ini sangat cocok dengan usingkonstruknya.
Yang lain sedikit lebih sulit. WaitEventHandlesuntuk contoh tidak digunakan seperti ini karena mereka digunakan untuk memberi sinyal dari satu utas ke yang lain. Pertanyaannya kemudian menjadi siapa yang harus memanggil Disposeini? Sebagai jenis perlindungan seperti ini menerapkan Finalizemetode, yang memastikan sumber daya dibuang ketika mesin virtual tidak lagi dirujuk oleh aplikasi.
Saya tidak dapat memahami jawaban yang disetujui ini. Saya masih ingin tahu bedanya. Apa itu?
Ismael
22
@ Ismail: Situasi terbesar di mana Finalizedapat dibenarkan adalah ketika ada sejumlah objek yang tertarik memiliki sumber daya tetap hidup, tetapi tidak ada cara di mana objek yang berhenti tertarik pada sumber daya dapat mengetahui apakah itu adalah terakhir. Dalam kasus seperti itu, Finalizebiasanya hanya akan menyala ketika tidak ada yang tertarik pada objek. Waktu yang longgar Finalizesangat buruk untuk sumber daya yang tidak dapat dipertukarkan seperti file dan kunci, tetapi mungkin tidak apa-apa untuk sumber daya yang sepadan.
supercat
13
+1 ke supercat untuk kata baru (untuk saya) yang hebat. Konteksnya membuatnya cukup jelas, tetapi untuk berjaga-jaga bagi kita semua, inilah yang dikatakan wikipedia: "Kesepadanan adalah properti dari barang atau komoditas yang unit-unit individualnya mampu saling menggantikan, seperti minyak mentah, saham di sebuah perusahaan, obligasi, logam mulia, atau mata uang. "
Jon Coombs
5
@ JonCoombs: Itu cukup benar, meskipun mungkin perlu dicatat bahwa istilah "sumber daya sepadan" diterapkan untuk hal-hal yang dapat diganti secara bebas sampai mereka diperoleh dan menjadi dapat diganti secara bebas lagi setelah rilis atau ditinggalkan . Jika sistem memiliki kumpulan objek kunci dan kode memperoleh satu yang dikaitkan dengan beberapa entitas, maka selama ada yang berpendapat bahwa referensi ke kunci itu untuk tujuan mengaitkannya dengan entitas itu , kunci itu mungkin tidak dapat diganti dengan ada yang lain. Jika semua kode yang peduli dengan entitas yang dijaga meninggalkan kunci, ...
supercat
... maka lagi akan menjadi bebas diganti sampai waktu seperti yang dikaitkan dengan beberapa entitas lain.
supercat
135
Metode finalizer dipanggil ketika objek Anda adalah sampah yang dikumpulkan dan Anda tidak memiliki jaminan kapan ini akan terjadi (Anda dapat memaksanya, tetapi itu akan merusak kinerja).
The DisposeMetode di sisi lain dimaksudkan untuk dipanggil oleh kode yang dibuat kelas Anda sehingga Anda dapat membersihkan dan melepaskan sumber daya yang Anda peroleh (data unmanaged, koneksi database, menangani file, dll) saat kode ini dilakukan dengan objekmu.
Praktik standar adalah menerapkan IDisposabledan Disposeagar Anda dapat menggunakan objek Anda dalam usingstatment. Seperti using(var foo = new MyObject()) { }. Dan di finalizer Anda, Anda menelepon Dispose, kalau-kalau kode panggilan lupa untuk membuang Anda.
Anda perlu sedikit berhati-hati untuk memanggil Buang dari implementasi Finalisasi Anda - Buang juga dapat membuang sumber daya yang dikelola, yang tidak ingin Anda sentuh dari finalizer Anda, karena mereka mungkin telah diselesaikan sendiri.
itowlson
6
@ itowlson: Memeriksa null dikombinasikan dengan asumsi bahwa objek dapat dibuang dua kali (dengan panggilan kedua tidak melakukan apa-apa) harus cukup baik.
Samuel
7
Pola IDisposal standar dan implementasi tersembunyi dari Buang (bool) untuk menangani membuang komponen yang dikelola opsional tampaknya memenuhi masalah itu.
Brody
Kedengarannya seperti tidak ada alasan untuk mengimplementasikan destructor (metode ~ MyClass ()) dan lebih suka selalu mengimplementasikan dan memanggil metode Buang (). Atau saya salah? Bisakah seseorang memberi saya contoh kapan keduanya harus diimplementasikan?
dpelisek
66
Finalisasi adalah metode backstop, yang dipanggil oleh pengumpul sampah saat mengambil kembali suatu objek. Buang adalah metode "deterministik pembersihan", yang dipanggil oleh aplikasi untuk melepaskan sumber daya asli yang berharga (pegangan jendela, koneksi basis data, dll.) Ketika mereka tidak lagi diperlukan, alih-alih membiarkannya ditahan tanpa batas waktu sampai GC berkeliling ke objek.
Sebagai pengguna suatu objek, Anda selalu menggunakan Buang. Finalisasi adalah untuk GC.
Sebagai pelaksana kelas, jika Anda memiliki sumber daya yang dikelola yang harus dibuang, Anda menerapkan Buang. Jika Anda memegang sumber daya asli, Anda menerapkan Buang dan Finalisasi, dan keduanya memanggil metode umum yang melepaskan sumber daya asli. Idiom ini biasanya digabungkan melalui metode Buang pribadi (membuang sampah), yang Buang panggilan dengan true, dan Finalisasi panggilan dengan false. Metode ini selalu membebaskan sumber daya asli, lalu memeriksa parameter pelepasan, dan jika benar ia membuang sumber daya yang dikelola dan memanggil GC.SuppressFinalize.
Pola asli yang direkomendasikan untuk kelas yang mengadakan campuran sumber daya swadaya ("dikelola") dan non-swadaya ("tidak dikelola") telah lama usang. Pola yang lebih baik adalah dengan membungkus secara terpisah setiap sumber daya yang tidak dikelola menjadi objek yang dikelola sendiri yang tidak memiliki referensi kuat untuk apa pun yang tidak diperlukan untuk pembersihannya. Segala sesuatu yang objek yang dapat diselesaikan memiliki referensi kuat langsung atau tidak langsung akan memperpanjang masa hidup GC-nya. Enkapsulasi hal-hal yang diperlukan untuk pembersihan akan memungkinkan seseorang menghindari memperpanjang masa hidup GC dari hal-hal yang tidak.
supercat
2
@JCoombs: Disposebagus, dan menerapkannya dengan benar pada umumnya mudah. Finalizeitu jahat, dan menerapkannya dengan benar biasanya sulit. Antara lain, karena GC akan memastikan bahwa identitas objek tidak akan pernah "didaur ulang" selama ada referensi ke objek itu, mudah untuk membersihkan banyak Disposableobjek, beberapa di antaranya mungkin sudah dibersihkan, adalah tidak masalah; setiap referensi ke objek yang Disposetelah dipanggil akan tetap menjadi referensi ke objek yang Disposetelah dipanggil.
supercat
2
@JCoombs: Sumber daya yang tidak dikelola, sebaliknya, umumnya tidak memiliki jaminan seperti itu. Jika objek Fredmemiliki pegangan file # 42 dan menutupnya, sistem mungkin melampirkan nomor yang sama ke beberapa pegangan file yang diberikan kepada beberapa entitas lain. Dalam hal ini, pegangan file # 42 tidak akan merujuk ke file tertutup Fred, tetapi ke file yang sedang digunakan aktif oleh entitas lain; karena Fredmencoba untuk menutup lagi # 42 akan menjadi bencana. Mencoba 100% andal melacak apakah satu objek yang tidak dikelola belum dirilis bisa diterapkan. Mencoba melacak beberapa objek jauh lebih sulit.
supercat
2
@JCoombs: Jika setiap sumber daya yang tidak dikelola ditempatkan di objek pembungkusnya sendiri yang tidak melakukan apa-apa selain mengontrol masa pakainya, maka di luar kode yang tidak tahu apakah sumber daya telah dirilis, tetapi tahu bahwa itu harus terjadi jika belum , dapat dengan aman meminta objek pembungkus untuk melepaskannya; objek wrapper akan tahu jika telah melakukannya dan dapat melaksanakan atau mengabaikan permintaan tersebut. Fakta bahwa GC menjamin bahwa referensi ke pembungkus akan selalu menjadi referensi yang valid ke pembungkus adalah jaminan yang sangat berguna .
supercat
43
Menyelesaikan
Finalizers harus selalu protected, bukan publicatau privateagar metode tidak dapat dipanggil dari kode aplikasi secara langsung dan pada saat yang sama, dapat membuat panggilan ke base.Finalizemetode
Finalizers harus melepaskan sumber daya yang tidak dikelola saja.
Kerangka kerja ini tidak menjamin bahwa finalizer akan melakukan sama sekali atas contoh yang diberikan.
Jangan pernah mengalokasikan memori dalam finalizers atau memanggil metode virtual dari finalizers.
Hindari sinkronisasi dan meningkatkan pengecualian yang tidak ditangani di finalizer.
Urutan eksekusi finalizer bersifat non-deterministik — dengan kata lain, Anda tidak dapat bergantung pada objek lain yang masih tersedia dalam finalizer Anda.
Jangan mendefinisikan finalizer pada tipe nilai.
Jangan buat destruktor kosong. Dengan kata lain, Anda seharusnya tidak pernah secara eksplisit mendefinisikan destructor kecuali kelas Anda perlu membersihkan sumber daya yang tidak dikelola dan jika Anda mendefinisikannya, itu harus melakukan beberapa pekerjaan. Jika, nanti, Anda tidak perlu lagi membersihkan sumber daya yang tidak dikelola di destruktor, hapus semuanya.
Membuang
Terapkan IDisposablepada setiap jenis yang memiliki finalizer
Pastikan bahwa suatu objek dibuat tidak dapat digunakan setelah melakukan panggilan ke Disposemetode. Dengan kata lain, hindari menggunakan objek setelah Disposemetode dipanggil.
Panggil Disposesemua IDisposablejenis setelah Anda selesai menggunakannya
Izinkan Disposeuntuk dipanggil beberapa kali tanpa menimbulkan kesalahan.
Menekan panggilan selanjutnya ke finalizer dari dalam Disposemetode menggunakan GC.SuppressFinalizemetode
Hindari membuat jenis nilai pakai
Hindari melemparkan pengecualian dari dalam Disposemetode
Buang / Selesaikan Pola
Microsoft merekomendasikan agar Anda menerapkan keduanya Disposedan Finalizeketika bekerja dengan sumber daya yang tidak dikelola. The Finalizepelaksanaan akan menjalankan dan sumber daya akan tetap dirilis ketika objek adalah sampah yang dikumpulkan bahkan jika pengembang diabaikan untuk memanggil Disposemetode secara eksplisit.
Bersihkan sumber daya yang tidak dikelola dalam Finalizemetode serta Disposemetode. Selain itu panggil Disposemetode untuk objek .NET yang Anda miliki sebagai komponen di dalam kelas itu (memiliki sumber daya yang tidak dikelola sebagai anggota mereka) dari Disposemetode.
Saya membaca jawaban yang sama di mana-mana dan saya masih tidak mengerti apa tujuan masing-masing. Saya hanya membaca aturan demi aturan, tidak lebih.
Ismael
@ Ismail: dan juga penulis tidak menambahkan apa pun kecuali untuk menyalin dan menempelkan beberapa teks dari MSDN.
Tarik
@tarik saya sudah mempelajarinya. Saya memang punya konsepsi "janji" waktu itu yang saya tanyakan ini.
Ismael
31
Finalisasi dipanggil oleh GC ketika objek ini tidak lagi digunakan.
Buang hanyalah metode normal yang dapat dipanggil oleh pengguna kelas ini untuk melepaskan sumber daya apa pun.
Jika pengguna lupa untuk memanggil Buang dan jika kelas telah menyelesaikan diterapkan maka GC akan memastikan itu dipanggil.
Ada beberapa kunci tentang dari buku MCSD Certification Toolkit (ujian 70-483) hal 193:
destructor ≈ (hampir sama dengan)base.Finalize() , destructor dikonversi menjadi versi override dari metode Finalisasi yang mengeksekusi kode destruktor dan kemudian memanggil metode Finalisasi kelas dasar. Maka itu sama sekali non deterministik yang Anda tidak bisa tahu kapan akan dipanggil karena tergantung pada GC.
Jika sebuah kelas tidak mengandung sumber daya yang dikelola dan tidak ada sumber daya yang tidak dikelola , itu tidak boleh menerapkan IDisposableatau memiliki destruktor.
Jika kelas hanya memiliki sumber daya yang dikelola , ia harus menerapkan IDisposabletetapi tidak boleh memiliki destruktor. (Ketika destructor dijalankan, Anda tidak dapat memastikan objek terkelola masih ada, jadi Anda tidak dapat memanggil Dispose()metode mereka .)
Jika kelas hanya memiliki sumber daya yang tidak dikelola , ia perlu mengimplementasikan IDisposabledan membutuhkan destruktor jika program tidak memanggil Dispose().
Dispose()Metode harus aman untuk dijalankan lebih dari sekali. Anda bisa mencapainya dengan menggunakan variabel untuk melacak apakah sudah dijalankan sebelumnya.
Dispose()harus membebaskan sumber daya yang dikelola dan tidak dikelola .
Destructor seharusnya membebaskan hanya sumber daya yang tidak dikelola . Ketika destructor dijalankan, Anda tidak dapat memastikan objek terkelola masih ada, jadi Anda tidak dapat memanggil metode Buang mereka. Ini diperoleh dengan menggunakan protected void Dispose(bool disposing)pola kanonik , di mana hanya sumber daya yang dikelola dibebaskan (dibuang) saat disposing == true.
Setelah membebaskan sumber daya, Dispose()harus meneleponGC.SuppressFinalize , sehingga objek dapat melewati antrian finalisasi.
Contoh implementasi untuk kelas dengan sumber daya yang tidak dikelola dan dikelola:
using System;classDisposableClass:IDisposable{// A name to keep track of the object.publicstringName="";// Free managed and unmanaged resources.publicvoidDispose(){FreeResources(true);// We don't need the destructor because// our resources are already freed.
GC.SuppressFinalize(this);}// Destructor to clean up unmanaged resources// but not managed resources.~DisposableClass(){FreeResources(false);}// Keep track if whether resources are already freed.privateboolResourcesAreFreed=false;// Free resources.privatevoidFreeResources(bool freeManagedResources){Console.WriteLine(Name+": FreeResources");if(!ResourcesAreFreed){// Dispose of managed resources if appropriate.if(freeManagedResources){// Dispose of managed resources here.Console.WriteLine(Name+": Dispose of managed resources");}// Dispose of unmanaged resources here.Console.WriteLine(Name+": Dispose of unmanaged resources");// Remember that we have disposed of resources.ResourcesAreFreed=true;}}}
Ini jawaban yang bagus! Tapi saya pikir ini salah: "destructor harus memanggil GC.SuppressFinalize". Sebaliknya, bukankah seharusnya metode Buang publik () memanggil GC.SuppressFinalize? Lihat: docs.microsoft.com/en-us/dotnet/api/... Memanggil metode ini mencegah pengumpul sampah memanggil Object.Finalize (yang ditimpa oleh destructor).
Ewa
7
99% dari waktu, Anda tidak perlu khawatir. :) Tapi, jika objek Anda menyimpan referensi ke sumber daya yang tidak dikelola (pegangan jendela, pegangan file, misalnya), Anda perlu menyediakan cara bagi objek yang dikelola untuk melepaskan sumber daya tersebut. Finalisasi memberikan kontrol implisit atas pelepasan sumber daya. Ini disebut oleh pemulung. Buang adalah cara untuk memberikan kontrol eksplisit atas pelepasan sumber daya dan dapat dipanggil langsung.
Ada banyak lagi yang bisa dipelajari tentang masalah Pengumpulan Sampah , tapi itu awal.
Saya cukup yakin lebih dari 1% dari aplikasi C # menggunakan database: di mana Anda harus khawatir tentang hal-hal SQL IDisposable.
Samuel
1
Juga, Anda harus menerapkan IDisposable jika Anda merangkum IDisposables. Yang mungkin mencakup 1% lainnya.
Darren Clark
@Samuel: Saya tidak melihat apa hubungannya dengan database. Jika Anda berbicara tentang menutup koneksi, itu baik-baik saja, tetapi itu masalah yang berbeda. Anda tidak perlu membuang objek untuk menutup koneksi pada waktu yang tepat.
JP Alioto
1
@ JP: Tapi pola Menggunakan (...) membuatnya jauh lebih mudah untuk diatasi.
Brody
2
Setuju, tapi itulah intinya. Pola penggunaan menyembunyikan panggilan untuk Buang untuk Anda.
JP Alioto
6
Finalizer adalah untuk pembersihan implisit - Anda harus menggunakannya setiap kali kelas mengelola sumber daya yang mutlak harus dibersihkan karena jika tidak, Anda akan bocor menangani / memori dll ...
Mengimplementasikan finalizer dengan benar sangat sulit dan harus dihindari sedapat mungkin - the SafeHandle kelas (tersedia dalam. Net v2.0 dan yang lebih tinggi) sekarang berarti bahwa Anda sangat jarang (jika pernah) perlu mengimplementasikan finalizer lagi.
Itu IDisposable interface untuk pembersihan eksplisit dan jauh lebih umum digunakan - Anda harus menggunakan ini untuk memungkinkan pengguna untuk secara eksplisit melepaskan atau sumber daya pembersihan setiap kali mereka telah selesai menggunakan obyek.
Perhatikan bahwa jika Anda memiliki finalizer maka Anda juga harus mengimplementasikan IDisposableantarmuka untuk memungkinkan pengguna untuk secara eksplisit melepaskan sumber daya tersebut lebih cepat daripada jika objek tersebut adalah sampah yang dikumpulkan.
Anda menulis finalizer untuk kelas Anda jika memiliki referensi ke sumber daya yang tidak dikelola dan Anda ingin memastikan bahwa sumber daya yang tidak dikelola dilepaskan ketika instance dari kelas itu adalah sampah yang dikumpulkan
secara otomatis . Perhatikan bahwa Anda tidak dapat memanggil Finalizer dari suatu objek secara eksplisit - itu disebut secara otomatis oleh pengumpul sampah jika diperlukan.
Di sisi lain, Anda menerapkan antarmuka IDisposable (dan akibatnya mendefinisikan metode Buang () sebagai hasil untuk kelas Anda) ketika kelas Anda memiliki referensi ke sumber daya yang tidak dikelola, tetapi Anda tidak ingin menunggu penampung sampah untuk menendang (yang bisa kapan saja - tidak dalam kendali programmer) dan ingin melepaskan sumber daya tersebut segera setelah Anda selesai. Dengan demikian, Anda dapat secara eksplisit melepaskan sumber daya yang tidak dikelola dengan memanggil metode Buang objek ().
Selain itu, perbedaan lainnya adalah - dalam implementasi Buang (), Anda harus melepaskan sumber daya yang dikelola juga , sedangkan yang tidak boleh dilakukan dalam Finalizer. Ini karena sangat mungkin bahwa sumber daya yang dikelola yang direferensikan oleh objek telah dibersihkan sebelum siap untuk diselesaikan.
Untuk kelas yang menggunakan sumber daya yang tidak dikelola, praktik terbaik adalah mendefinisikan keduanya - metode Buang () dan Finalizer - untuk digunakan sebagai mundur jika pengembang lupa untuk secara eksplisit membuang objek. Keduanya dapat menggunakan metode bersama untuk membersihkan sumber daya yang dikelola dan tidak dikelola: -
classClassWithDisposeAndFinalize:IDisposable{// Used to determine if Dispose() has already been called, so that the finalizer// knows if it needs to clean up unmanaged resources.privatebool disposed =false;publicvoidDispose(){// Call our shared helper method.// Specifying "true" signifies that the object user triggered the cleanup.CleanUp(true);// Now suppress finalization to make sure that the Finalize method // doesn't attempt to clean up unmanaged resources.
GC.SuppressFinalize(this);}privatevoidCleanUp(bool disposing){// Be sure we have not already been disposed!if(!this.disposed){// If disposing equals true i.e. if disposed explicitly, dispose all // managed resources.if(disposing){// Dispose managed resources.}// Clean up unmanaged resources here.}
disposed =true;}// the below is called the destructor or Finalizer~ClassWithDisposeAndFinalize(){// Call our shared helper method.// Specifying "false" signifies that the GC triggered the cleanup.CleanUp(false);}
Perbedaan antara Finalisasi dan Buang metode dalam C #.
GC memanggil metode finalisasi untuk memperoleh kembali sumber daya yang tidak dikelola (seperti operasi file, jendela api, koneksi jaringan, koneksi database) tetapi waktu tidak diperbaiki ketika GC akan memanggilnya. Disebut secara implisit oleh GC itu berarti kita tidak memiliki kontrol tingkat rendah di atasnya.
Buang Metode: Kami memiliki kontrol level rendah ketika kami menyebutnya dari kode. kami dapat memperoleh kembali sumber daya yang tidak dikelola kapan pun kami merasa tidak dapat digunakan. Kami dapat mencapainya dengan menerapkan pola IDisposal.
Kelas instance sering merangkum kontrol atas sumber daya yang tidak dikelola oleh runtime, seperti pegangan jendela (HWND), koneksi database, dan sebagainya. Oleh karena itu, Anda harus menyediakan cara eksplisit dan implisit untuk membebaskan sumber daya tersebut. Berikan kontrol implisit dengan menerapkan Metode Finalisasi yang dilindungi pada objek (sintaks destruktor dalam C # dan Ekstensi yang Dikelola untuk C ++). Pengumpul sampah memanggil metode ini di beberapa titik setelah tidak ada lagi referensi yang valid ke objek. Dalam beberapa kasus, Anda mungkin ingin memberi pemrogram menggunakan objek dengan kemampuan untuk secara eksplisit melepaskan sumber daya eksternal ini sebelum pengumpul sampah membebaskan objek. Jika sumber daya eksternal langka atau mahal, kinerja yang lebih baik dapat dicapai jika programmer secara eksplisit melepaskan sumber daya ketika mereka tidak lagi digunakan. Untuk memberikan kontrol eksplisit, implementasikan metode Buang yang disediakan oleh IDisposable Interface. Konsumen objek harus memanggil metode ini ketika selesai menggunakan objek. Buang dapat dipanggil bahkan jika referensi lain ke objek masih hidup.
Perhatikan bahwa bahkan ketika Anda memberikan kontrol eksplisit melalui Buang, Anda harus memberikan pembersihan implisit menggunakan metode Finalisasi. Finalisasi menyediakan cadangan untuk mencegah sumber bocor secara permanen jika pemrogram gagal menelepon Buang.
Perbedaan utama antara Buang dan Finalisasi adalah:
Disposebiasanya dipanggil oleh kode Anda. Sumber daya dibebaskan secara instan saat Anda menyebutnya. Orang lupa memanggil metode, jadi using() {}pernyataan ditemukan. Ketika program Anda menyelesaikan eksekusi kode di dalamnya {}, ia akan memanggil Disposemetode secara otomatis.
Finalizetidak dipanggil oleh kode Anda. Ini berarti dipanggil oleh Pengumpul Sampah (GC). Itu berarti sumber daya dapat dibebaskan kapan saja di masa depan kapan pun GC memutuskan untuk melakukannya. Ketika GC melakukan tugasnya, ia akan melalui banyak metode Finalisasi. Jika Anda memiliki logika yang berat dalam hal ini, itu akan membuat prosesnya lambat. Ini dapat menyebabkan masalah kinerja untuk program Anda. Jadi berhati-hatilah dengan apa yang Anda masukkan ke sana.
Saya pribadi akan menulis sebagian besar logika penghancuran di Buang. Mudah-mudahan, ini menghilangkan kebingungan.
Seperti yang kita ketahui, buang dan selesaikan keduanya digunakan untuk membebaskan sumber daya yang tidak dikelola .. tetapi perbedaannya adalah selesaikan menggunakan dua siklus untuk membebaskan sumber daya, sedangkan buangnya gunakan satu siklus ..
Buang segera bebaskan sumber daya . Finalisasi mungkin atau mungkin tidak membebaskan sumber daya dengan tingkat ketepatan waktu apa pun.
supercat,
1
Ah, dia kemungkinan berarti "objek yang dapat diselesaikan ini perlu dideteksi oleh GC dua kali sebelum ingatannya kembali", baca lebih lanjut di sini: ericlippert.com/2015/05/05/18/…
aeroson
-4
Untuk menjawab pada bagian pertama, Anda harus memberikan contoh di mana orang menggunakan pendekatan berbeda untuk objek kelas yang sama persis. Kalau tidak, sulit (atau bahkan aneh) untuk menjawab.
Dengan kata lain: GC hanya tahu tentang finalizer (jika ada. Juga dikenal sebagai destruktor ke Microsoft). Kode yang baik akan berusaha untuk membersihkan dari keduanya (finalizer dan Buang).
Jawaban:
Yang lain sudah membahas perbedaan antara
Dispose
danFinalize
(btwFinalize
metode ini masih disebut destructor dalam spesifikasi bahasa), jadi saya hanya akan menambahkan sedikit tentang skenario di manaFinalize
metode ini berguna.Beberapa jenis merangkum sumber daya sekali pakai dengan cara yang mudah digunakan dan membuangnya dalam satu tindakan. Penggunaan umum sering seperti ini: buka, baca atau tulis, tutup (Buang). Ini sangat cocok dengan
using
konstruknya.Yang lain sedikit lebih sulit.
WaitEventHandles
untuk contoh tidak digunakan seperti ini karena mereka digunakan untuk memberi sinyal dari satu utas ke yang lain. Pertanyaannya kemudian menjadi siapa yang harus memanggilDispose
ini? Sebagai jenis perlindungan seperti ini menerapkanFinalize
metode, yang memastikan sumber daya dibuang ketika mesin virtual tidak lagi dirujuk oleh aplikasi.sumber
Finalize
dapat dibenarkan adalah ketika ada sejumlah objek yang tertarik memiliki sumber daya tetap hidup, tetapi tidak ada cara di mana objek yang berhenti tertarik pada sumber daya dapat mengetahui apakah itu adalah terakhir. Dalam kasus seperti itu,Finalize
biasanya hanya akan menyala ketika tidak ada yang tertarik pada objek. Waktu yang longgarFinalize
sangat buruk untuk sumber daya yang tidak dapat dipertukarkan seperti file dan kunci, tetapi mungkin tidak apa-apa untuk sumber daya yang sepadan.Metode finalizer dipanggil ketika objek Anda adalah sampah yang dikumpulkan dan Anda tidak memiliki jaminan kapan ini akan terjadi (Anda dapat memaksanya, tetapi itu akan merusak kinerja).
The
Dispose
Metode di sisi lain dimaksudkan untuk dipanggil oleh kode yang dibuat kelas Anda sehingga Anda dapat membersihkan dan melepaskan sumber daya yang Anda peroleh (data unmanaged, koneksi database, menangani file, dll) saat kode ini dilakukan dengan objekmu.Praktik standar adalah menerapkan
IDisposable
danDispose
agar Anda dapat menggunakan objek Anda dalamusing
statment. Sepertiusing(var foo = new MyObject()) { }
. Dan di finalizer Anda, Anda meneleponDispose
, kalau-kalau kode panggilan lupa untuk membuang Anda.sumber
Finalisasi adalah metode backstop, yang dipanggil oleh pengumpul sampah saat mengambil kembali suatu objek. Buang adalah metode "deterministik pembersihan", yang dipanggil oleh aplikasi untuk melepaskan sumber daya asli yang berharga (pegangan jendela, koneksi basis data, dll.) Ketika mereka tidak lagi diperlukan, alih-alih membiarkannya ditahan tanpa batas waktu sampai GC berkeliling ke objek.
Sebagai pengguna suatu objek, Anda selalu menggunakan Buang. Finalisasi adalah untuk GC.
Sebagai pelaksana kelas, jika Anda memiliki sumber daya yang dikelola yang harus dibuang, Anda menerapkan Buang. Jika Anda memegang sumber daya asli, Anda menerapkan Buang dan Finalisasi, dan keduanya memanggil metode umum yang melepaskan sumber daya asli. Idiom ini biasanya digabungkan melalui metode Buang pribadi (membuang sampah), yang Buang panggilan dengan true, dan Finalisasi panggilan dengan false. Metode ini selalu membebaskan sumber daya asli, lalu memeriksa parameter pelepasan, dan jika benar ia membuang sumber daya yang dikelola dan memanggil GC.SuppressFinalize.
sumber
Dispose
bagus, dan menerapkannya dengan benar pada umumnya mudah.Finalize
itu jahat, dan menerapkannya dengan benar biasanya sulit. Antara lain, karena GC akan memastikan bahwa identitas objek tidak akan pernah "didaur ulang" selama ada referensi ke objek itu, mudah untuk membersihkan banyakDisposable
objek, beberapa di antaranya mungkin sudah dibersihkan, adalah tidak masalah; setiap referensi ke objek yangDispose
telah dipanggil akan tetap menjadi referensi ke objek yangDispose
telah dipanggil.Fred
memiliki pegangan file # 42 dan menutupnya, sistem mungkin melampirkan nomor yang sama ke beberapa pegangan file yang diberikan kepada beberapa entitas lain. Dalam hal ini, pegangan file # 42 tidak akan merujuk ke file tertutup Fred, tetapi ke file yang sedang digunakan aktif oleh entitas lain; karenaFred
mencoba untuk menutup lagi # 42 akan menjadi bencana. Mencoba 100% andal melacak apakah satu objek yang tidak dikelola belum dirilis bisa diterapkan. Mencoba melacak beberapa objek jauh lebih sulit.Menyelesaikan
protected
, bukanpublic
atauprivate
agar metode tidak dapat dipanggil dari kode aplikasi secara langsung dan pada saat yang sama, dapat membuat panggilan kebase.Finalize
metodeMembuang
IDisposable
pada setiap jenis yang memiliki finalizerDispose
metode. Dengan kata lain, hindari menggunakan objek setelahDispose
metode dipanggil.Dispose
semuaIDisposable
jenis setelah Anda selesai menggunakannyaDispose
untuk dipanggil beberapa kali tanpa menimbulkan kesalahan.Dispose
metode menggunakanGC.SuppressFinalize
metodeDispose
metodeBuang / Selesaikan Pola
Dispose
danFinalize
ketika bekerja dengan sumber daya yang tidak dikelola. TheFinalize
pelaksanaan akan menjalankan dan sumber daya akan tetap dirilis ketika objek adalah sampah yang dikumpulkan bahkan jika pengembang diabaikan untuk memanggilDispose
metode secara eksplisit.Finalize
metode sertaDispose
metode. Selain itu panggilDispose
metode untuk objek .NET yang Anda miliki sebagai komponen di dalam kelas itu (memiliki sumber daya yang tidak dikelola sebagai anggota mereka) dariDispose
metode.sumber
Finalisasi dipanggil oleh GC ketika objek ini tidak lagi digunakan.
Buang hanyalah metode normal yang dapat dipanggil oleh pengguna kelas ini untuk melepaskan sumber daya apa pun.
Jika pengguna lupa untuk memanggil Buang dan jika kelas telah menyelesaikan diterapkan maka GC akan memastikan itu dipanggil.
sumber
Ada beberapa kunci tentang dari buku MCSD Certification Toolkit (ujian 70-483) hal 193:
destructor ≈ (hampir sama dengan)
base.Finalize()
, destructor dikonversi menjadi versi override dari metode Finalisasi yang mengeksekusi kode destruktor dan kemudian memanggil metode Finalisasi kelas dasar. Maka itu sama sekali non deterministik yang Anda tidak bisa tahu kapan akan dipanggil karena tergantung pada GC.Jika sebuah kelas tidak mengandung sumber daya yang dikelola dan tidak ada sumber daya yang tidak dikelola , itu tidak boleh menerapkan
IDisposable
atau memiliki destruktor.Jika kelas hanya memiliki sumber daya yang dikelola , ia harus menerapkan
IDisposable
tetapi tidak boleh memiliki destruktor. (Ketika destructor dijalankan, Anda tidak dapat memastikan objek terkelola masih ada, jadi Anda tidak dapat memanggilDispose()
metode mereka .)Jika kelas hanya memiliki sumber daya yang tidak dikelola , ia perlu mengimplementasikan
IDisposable
dan membutuhkan destruktor jika program tidak memanggilDispose()
.Dispose()
Metode harus aman untuk dijalankan lebih dari sekali. Anda bisa mencapainya dengan menggunakan variabel untuk melacak apakah sudah dijalankan sebelumnya.Dispose()
harus membebaskan sumber daya yang dikelola dan tidak dikelola .Destructor seharusnya membebaskan hanya sumber daya yang tidak dikelola . Ketika destructor dijalankan, Anda tidak dapat memastikan objek terkelola masih ada, jadi Anda tidak dapat memanggil metode Buang mereka. Ini diperoleh dengan menggunakan
protected void Dispose(bool disposing)
pola kanonik , di mana hanya sumber daya yang dikelola dibebaskan (dibuang) saatdisposing == true
.Setelah membebaskan sumber daya,
Dispose()
harus meneleponGC.SuppressFinalize
, sehingga objek dapat melewati antrian finalisasi.Contoh implementasi untuk kelas dengan sumber daya yang tidak dikelola dan dikelola:
sumber
99% dari waktu, Anda tidak perlu khawatir. :) Tapi, jika objek Anda menyimpan referensi ke sumber daya yang tidak dikelola (pegangan jendela, pegangan file, misalnya), Anda perlu menyediakan cara bagi objek yang dikelola untuk melepaskan sumber daya tersebut. Finalisasi memberikan kontrol implisit atas pelepasan sumber daya. Ini disebut oleh pemulung. Buang adalah cara untuk memberikan kontrol eksplisit atas pelepasan sumber daya dan dapat dipanggil langsung.
Ada banyak lagi yang bisa dipelajari tentang masalah Pengumpulan Sampah , tapi itu awal.
sumber
Finalizer adalah untuk pembersihan implisit - Anda harus menggunakannya setiap kali kelas mengelola sumber daya yang mutlak harus dibersihkan karena jika tidak, Anda akan bocor menangani / memori dll ...
Mengimplementasikan finalizer dengan benar sangat sulit dan harus dihindari sedapat mungkin - the
SafeHandle
kelas (tersedia dalam. Net v2.0 dan yang lebih tinggi) sekarang berarti bahwa Anda sangat jarang (jika pernah) perlu mengimplementasikan finalizer lagi.Itu
IDisposable
interface untuk pembersihan eksplisit dan jauh lebih umum digunakan - Anda harus menggunakan ini untuk memungkinkan pengguna untuk secara eksplisit melepaskan atau sumber daya pembersihan setiap kali mereka telah selesai menggunakan obyek.Perhatikan bahwa jika Anda memiliki finalizer maka Anda juga harus mengimplementasikan
IDisposable
antarmuka untuk memungkinkan pengguna untuk secara eksplisit melepaskan sumber daya tersebut lebih cepat daripada jika objek tersebut adalah sampah yang dikumpulkan.Lihat Pembaruan DG: Buang, Finalisasi, dan Manajemen Sumber Daya untuk apa yang saya anggap sebagai rekomendasi terbaik dan terlengkap untuk para finalizer dan
IDisposable
.sumber
Ringkasannya adalah -
Selain itu, perbedaan lainnya adalah - dalam implementasi Buang (), Anda harus melepaskan sumber daya yang dikelola juga , sedangkan yang tidak boleh dilakukan dalam Finalizer. Ini karena sangat mungkin bahwa sumber daya yang dikelola yang direferensikan oleh objek telah dibersihkan sebelum siap untuk diselesaikan.
Untuk kelas yang menggunakan sumber daya yang tidak dikelola, praktik terbaik adalah mendefinisikan keduanya - metode Buang () dan Finalizer - untuk digunakan sebagai mundur jika pengembang lupa untuk secara eksplisit membuang objek. Keduanya dapat menggunakan metode bersama untuk membersihkan sumber daya yang dikelola dan tidak dikelola: -
sumber
Contoh terbaik yang saya tahu.
sumber
Perbedaan antara Finalisasi dan Buang metode dalam C #.
GC memanggil metode finalisasi untuk memperoleh kembali sumber daya yang tidak dikelola (seperti operasi file, jendela api, koneksi jaringan, koneksi database) tetapi waktu tidak diperbaiki ketika GC akan memanggilnya. Disebut secara implisit oleh GC itu berarti kita tidak memiliki kontrol tingkat rendah di atasnya.
Buang Metode: Kami memiliki kontrol level rendah ketika kami menyebutnya dari kode. kami dapat memperoleh kembali sumber daya yang tidak dikelola kapan pun kami merasa tidak dapat digunakan. Kami dapat mencapainya dengan menerapkan pola IDisposal.
sumber
Kelas instance sering merangkum kontrol atas sumber daya yang tidak dikelola oleh runtime, seperti pegangan jendela (HWND), koneksi database, dan sebagainya. Oleh karena itu, Anda harus menyediakan cara eksplisit dan implisit untuk membebaskan sumber daya tersebut. Berikan kontrol implisit dengan menerapkan Metode Finalisasi yang dilindungi pada objek (sintaks destruktor dalam C # dan Ekstensi yang Dikelola untuk C ++). Pengumpul sampah memanggil metode ini di beberapa titik setelah tidak ada lagi referensi yang valid ke objek. Dalam beberapa kasus, Anda mungkin ingin memberi pemrogram menggunakan objek dengan kemampuan untuk secara eksplisit melepaskan sumber daya eksternal ini sebelum pengumpul sampah membebaskan objek. Jika sumber daya eksternal langka atau mahal, kinerja yang lebih baik dapat dicapai jika programmer secara eksplisit melepaskan sumber daya ketika mereka tidak lagi digunakan. Untuk memberikan kontrol eksplisit, implementasikan metode Buang yang disediakan oleh IDisposable Interface. Konsumen objek harus memanggil metode ini ketika selesai menggunakan objek. Buang dapat dipanggil bahkan jika referensi lain ke objek masih hidup.
Perhatikan bahwa bahkan ketika Anda memberikan kontrol eksplisit melalui Buang, Anda harus memberikan pembersihan implisit menggunakan metode Finalisasi. Finalisasi menyediakan cadangan untuk mencegah sumber bocor secara permanen jika pemrogram gagal menelepon Buang.
sumber
Perbedaan utama antara Buang dan Finalisasi adalah:
Dispose
biasanya dipanggil oleh kode Anda. Sumber daya dibebaskan secara instan saat Anda menyebutnya. Orang lupa memanggil metode, jadiusing() {}
pernyataan ditemukan. Ketika program Anda menyelesaikan eksekusi kode di dalamnya{}
, ia akan memanggilDispose
metode secara otomatis.Finalize
tidak dipanggil oleh kode Anda. Ini berarti dipanggil oleh Pengumpul Sampah (GC). Itu berarti sumber daya dapat dibebaskan kapan saja di masa depan kapan pun GC memutuskan untuk melakukannya. Ketika GC melakukan tugasnya, ia akan melalui banyak metode Finalisasi. Jika Anda memiliki logika yang berat dalam hal ini, itu akan membuat prosesnya lambat. Ini dapat menyebabkan masalah kinerja untuk program Anda. Jadi berhati-hatilah dengan apa yang Anda masukkan ke sana.Saya pribadi akan menulis sebagian besar logika penghancuran di Buang. Mudah-mudahan, ini menghilangkan kebingungan.
sumber
Seperti yang kita ketahui, buang dan selesaikan keduanya digunakan untuk membebaskan sumber daya yang tidak dikelola .. tetapi perbedaannya adalah selesaikan menggunakan dua siklus untuk membebaskan sumber daya, sedangkan buangnya gunakan satu siklus ..
sumber
Untuk menjawab pada bagian pertama, Anda harus memberikan contoh di mana orang menggunakan pendekatan berbeda untuk objek kelas yang sama persis. Kalau tidak, sulit (atau bahkan aneh) untuk menjawab.
Sedangkan untuk pertanyaan kedua sebaiknya baca dulu ini penggunaan yang benar dari antarmuka IDisposable yang mengklaim itu
Dengan kata lain: GC hanya tahu tentang finalizer (jika ada. Juga dikenal sebagai destruktor ke Microsoft). Kode yang baik akan berusaha untuk membersihkan dari keduanya (finalizer dan Buang).
sumber