Jawaban:
Yang lain sudah membahas perbedaan antara Dispose
dan Finalize
(btw Finalize
metode ini masih disebut destructor dalam spesifikasi bahasa), jadi saya hanya akan menambahkan sedikit tentang skenario di mana Finalize
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 memanggil Dispose
ini? Sebagai jenis perlindungan seperti ini menerapkan Finalize
metode, yang memastikan sumber daya dibuang ketika mesin virtual tidak lagi dirujuk oleh aplikasi.
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 longgar Finalize
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
dan Dispose
agar Anda dapat menggunakan objek Anda dalam using
statment. Seperti using(var foo = new MyObject()) { }
. Dan di finalizer Anda, Anda menelepon Dispose
, kalau-kalau kode panggilan lupa untuk membuang Anda.
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.
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 banyak Disposable
objek, beberapa di antaranya mungkin sudah dibersihkan, adalah tidak masalah; setiap referensi ke objek yang Dispose
telah dipanggil akan tetap menjadi referensi ke objek yang Dispose
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; karena Fred
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
, bukan public
atau private
agar metode tidak dapat dipanggil dari kode aplikasi secara langsung dan pada saat yang sama, dapat membuat panggilan ke base.Finalize
metodeMembuang
IDisposable
pada setiap jenis yang memiliki finalizerDispose
metode. Dengan kata lain, hindari menggunakan objek setelah Dispose
metode dipanggil.Dispose
semua IDisposable
jenis setelah Anda selesai menggunakannyaDispose
untuk dipanggil beberapa kali tanpa menimbulkan kesalahan.Dispose
metode menggunakan GC.SuppressFinalize
metodeDispose
metodeBuang / Selesaikan Pola
Dispose
dan Finalize
ketika bekerja dengan sumber daya yang tidak dikelola. The Finalize
pelaksanaan akan menjalankan dan sumber daya akan tetap dirilis ketika objek adalah sampah yang dikumpulkan bahkan jika pengembang diabaikan untuk memanggil Dispose
metode secara eksplisit.Finalize
metode serta Dispose
metode. Selain itu panggil Dispose
metode untuk objek .NET yang Anda miliki sebagai komponen di dalam kelas itu (memiliki sumber daya yang tidak dikelola sebagai anggota mereka) dari Dispose
metode.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 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 memanggil Dispose()
metode mereka .)
Jika kelas hanya memiliki sumber daya yang tidak dikelola , ia perlu mengimplementasikan IDisposable
dan 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;
class DisposableClass : IDisposable
{
// A name to keep track of the object.
public string Name = "";
// Free managed and unmanaged resources.
public void Dispose()
{
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.
private bool ResourcesAreFreed = false;
// Free resources.
private void FreeResources(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;
}
}
}
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.
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
.
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: -
class ClassWithDisposeAndFinalize : IDisposable
{
// Used to determine if Dispose() has already been called, so that the finalizer
// knows if it needs to clean up unmanaged resources.
private bool disposed = false;
public void Dispose()
{
// 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);
}
private void CleanUp(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);
}
Contoh terbaik yang saya tahu.
public abstract class DisposableType: IDisposable
{
bool disposed = false;
~DisposableType()
{
if (!disposed)
{
disposed = true;
Dispose(false);
}
}
public void Dispose()
{
if (!disposed)
{
disposed = true;
Dispose(true);
GC.SuppressFinalize(this);
}
}
public void Close()
{
Dispose();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// managed objects
}
// unmanaged objects and resources
}
}
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:
Dispose
biasanya 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 Dispose
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.
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 ..
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
Itu pilihanmu! Tapi pilih Buang.
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).