Kapan saya harus membuat destruktor?

185

Sebagai contoh:

public class Person
{
    public Person()
    {
    }

    ~Person()
    {
    }
}

Kapan saya harus membuat destruktor secara manual? Kapan Anda perlu membuat destruktor?

David Heffernan
sumber
1
Bahasa C # menyebut ini "destruktor", tetapi kebanyakan orang menyebutnya "finalizers" karena itu adalah nama .NET mereka dan mengurangi kebingungan dengan destruktor C ++ (yang sangat berbeda). Cara Menerapkan IDisposable dan Finalizers: 3 Aturan Mudah
Stephen Cleary
5
Saat Anda merasa gegabah.
capdragon
32
Saya pikir keyboard TomTom tidak berfungsi. Kunci caps secara sporadis aktif dan nonaktif. Aneh.
Jeff LaFay
lihat juga stackoverflow.com/questions/1076965/…
Ian Ringrose
Saya akhirnya menggunakan destructor sebagai alat bantu debug berdasarkan saran Greg Beech: stackoverflow.com/questions/3832911/…
Brian

Jawaban:

237

UPDATE: Pertanyaan ini adalah topik blog saya pada bulan Mei 2015 . Terima kasih atas pertanyaannya! Lihat blog untuk daftar panjang kepalsuan yang orang percayai tentang finalisasi.

Kapan saya harus membuat destruktor secara manual?

Hampir tidak pernah.

Biasanya satu hanya menciptakan destruktor ketika kelas Anda berpegang pada beberapa sumber daya tidak terkelola mahal yang harus dibersihkan ketika objek hilang. Lebih baik menggunakan pola sekali pakai untuk memastikan bahwa sumber daya dibersihkan. Destructor pada dasarnya adalah sebuah jaminan bahwa jika konsumen objek Anda lupa untuk membuangnya, sumber daya itu akan tetap dibersihkan pada akhirnya. (Mungkin.)

Jika Anda membuat destruktor, berhati - hatilah dan pahami cara kerja pemulung . Destructors sangat aneh :

  • Mereka tidak berjalan di utas Anda; mereka berlari di utas mereka sendiri. Jangan menyebabkan kebuntuan!
  • Pengecualian yang tidak tertangani yang dilemparkan dari destruktor adalah berita buruk. Itu di utasnya sendiri; siapa yang akan menangkapnya?
  • Destruktor dapat dipanggil pada objek setelah konstruktor dimulai tetapi sebelum konstruktor selesai. Destruktor yang ditulis dengan benar tidak akan bergantung pada invarian yang dibuat dalam konstruktor.
  • Destruktor dapat "menghidupkan kembali" suatu objek, membuat objek mati hidup kembali. Itu sangat aneh. Jangan lakukan itu.
  • Sebuah destructor mungkin tidak pernah berjalan; Anda tidak dapat mengandalkan objek yang pernah dijadwalkan untuk finalisasi. Ini mungkin akan menjadi, tapi itu bukan jaminan.

Hampir tidak ada yang biasanya benar dalam destruktor. Berhati-hatilah. Menulis destruktor yang benar sangat sulit.

Kapan Anda perlu membuat destruktor?

Saat menguji bagian dari kompiler yang menangani destruktor. Saya tidak pernah perlu melakukannya dalam kode produksi. Saya jarang menulis objek yang memanipulasi sumber daya yang tidak dikelola.

Eric Lippert
sumber
"Seorang destruktor dapat dipanggil pada suatu objek setelah konstruktor dimulai tetapi sebelum konstruktor selesai." Tapi saya bisa mengandalkan inisialisasi lapangan untuk menjalankan, kan?
konfigurator
13
@configurator: Tidak. Misalkan penginisialisasi bidang ketiga dari suatu objek dengan finalizer disebut metode statis yang menyebabkan pengecualian dilemparkan. Kapan inisialisasi bidang keempat dijalankan? Tidak pernah. Tetapi objek tersebut masih dialokasikan dan harus diselesaikan. Heck, Anda bahkan tidak memiliki jaminan bahwa bidang tipe ganda sepenuhnya diinisialisasi ketika dtor berjalan. Mungkin ada utas dibatalkan di tengah menulis ganda dan sekarang finalizer harus berurusan dengan setengah nol setengah diinisialisasi.
Eric Lippert
1
Pos luar biasa, tetapi seharusnya mengatakan "harus dibuat ketika kelas Anda memegang beberapa objek tidak terkelola yang mahal atau menyebabkan sejumlah besar objek yang tidak dikelola" - Sebagai contoh konkret, saya memiliki kelas matriks dalam C # yang menggunakan C + asli yang mendasari kelas matriks untuk melakukan banyak pengangkatan berat - Saya membuat banyak matriks - "penghancur" jauh lebih unggul daripada IDisposable dalam kasus khusus ini, karena membuat sisi rumah yang dikelola dan tidak dikelola dalam sinkronisasi yang lebih baik
Mark Mullin
1
pythonnet menggunakan destructor untuk melepaskan GIL dalam CPython yang tidak dikelola
denfromufa
3
Artikel yang luar biasa Eric. Alat peraga untuk ini -> "Kegembiraan bonus tambahan: runtime menggunakan pembuatan kode yang kurang agresif dan pengumpulan sampah yang kurang agresif ketika menjalankan program di debugger, karena merupakan pengalaman debug yang buruk untuk memiliki objek yang sedang Anda debug tiba-tiba menghilang meskipun variabel yang merujuk ke objek berada dalam cakupan. Itu berarti bahwa jika Anda memiliki bug di mana objek diselesaikan terlalu dini, Anda mungkin tidak dapat mereproduksi bug itu di debugger! "
Ken Palmer
17

Ini disebut "finalizer", dan Anda biasanya hanya harus membuat satu untuk kelas yang statusnya (yaitu: bidang) mencakup sumber daya yang tidak dikelola (yaitu: pointer untuk menangani diambil melalui p / memanggil panggilan). Namun, di .NET 2.0 dan yang lebih baru, sebenarnya ada cara yang lebih baik untuk menangani pembersihan sumber daya yang tidak dikelola: SafeHandle . Dengan ini, Anda seharusnya tidak perlu menulis finalizer lagi.

Nicole Calinoiu
sumber
25
@ ThomasEding - Ya itu . C # menggunakan sintaks destruktor, tetapi sebenarnya membuat finalizer . Lagi .
JDB masih mengingat Monica
@ JDB: Konstruk linguistik disebut destruktor. Saya tidak suka namanya, tapi itu namanya. Tindakan mendeklarasikan destruktor menyebabkan compiler untuk menghasilkan metode finalizer yang berisi sedikit kode wrapper bersama dengan apa pun yang muncul di tubuh destruktor.
supercat
8

Anda tidak memerlukannya kecuali kelas Anda mengelola sumber daya yang tidak dikelola seperti pegangan file Windows.

John Saunders
sumber
5
Sebenarnya, ini disebut destruktor
David Heffernan
2
Sekarang aku bingung. Apakah itu finalizer atau destructor?
4
C # spec sebenarnya menyebutnya sebagai destruktor. Beberapa orang melihat ini sebagai kesalahan. stackoverflow.com/questions/1872700/…
Ani
2
@ ThomasEding - Ya itu . C # menggunakan sintaks destruktor, tetapi sebenarnya membuat finalizer .
JDB masih mengingat Monica
2
Saya suka komentar di sini, panto asli :)
Benjol
4

Ini disebut destructor / finalizer, dan biasanya dibuat saat menerapkan pola Disposed.

Ini adalah solusi mundur ketika pengguna kelas Anda lupa menelepon Buang, untuk memastikan bahwa (akhirnya) sumber daya Anda dilepaskan, tetapi Anda tidak memiliki jaminan kapan pun destruktor dipanggil.

Dalam pertanyaan Stack Overflow ini , jawaban yang diterima dengan benar menunjukkan bagaimana menerapkan pola buang. Ini hanya diperlukan jika kelas Anda mengandung sumber daya tidak tertangani yang tidak dikelola oleh pengumpul sampah untuk membersihkannya sendiri.

Praktik yang baik adalah tidak mengimplementasikan finalizer tanpa juga memberi pengguna kelas kemungkinan untuk secara manual Membuang objek untuk membebaskan sumber daya segera.

Øyvind Bråthen
sumber
Sebenarnya BUKAN disebut destruktor di C # dengan alasan yang bagus.
TomTom
14
Sebenarnya itu . Terima kasih telah memberi saya downvote karena Anda salah. Lihat perpustakaan MSDN mengenai masalah khusus ini: msdn.microsoft.com/en-us/library/66x5fx1b.aspx
Øyvind Bråthen
1
@ TomTom, nama resminya adalah destruktor
David Heffernan
Ini sebenarnya bukan metode mundur, itu hanya memungkinkan GC untuk mengelola ketika objek Anda membebaskan sumber daya yang tidak dikelola, menerapkan IDisposable memungkinkan Anda untuk mengelolanya sendiri.
HasaniH
3

Ketika Anda memiliki sumber daya yang tidak dikelola dan Anda perlu memastikan mereka akan dibersihkan ketika objek Anda hilang. Contoh yang bagus adalah objek COM atau Penangan File.

Vlad Bezden
sumber
2

Saya telah menggunakan destruktor (hanya untuk keperluan debug) untuk melihat apakah suatu objek sedang dibersihkan dari memori dalam lingkup aplikasi WPF. Saya tidak yakin apakah pengumpulan sampah benar-benar membersihkan objek dari memori, dan ini adalah cara yang baik untuk memverifikasi.

Neil. Semua
sumber
1

Destructors menyediakan cara implisit untuk membebaskan sumber daya yang tidak dikelola yang dikemas dalam kelas Anda, mereka dipanggil ketika GC menyiasatinya dan mereka secara implisit memanggil metode Finalisasi dari kelas dasar. Jika Anda menggunakan banyak sumber daya yang tidak dikelola, lebih baik menyediakan cara eksplisit untuk membebaskan sumber daya tersebut melalui antarmuka IDisposable. Lihat panduan pemrograman C #: http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx

HasaniH
sumber