Memeriksa finalizer kelas

Selesai

Finalizer (secara historis disebut sebagai destruktor) digunakan untuk melakukan pembersihan akhir yang diperlukan ketika instans kelas dikumpulkan oleh pengumpul sampah (GC). Dalam kebanyakan kasus, Anda dapat menghindari penulisan finalizer dengan menggunakan kelas System.Runtime.InteropServices.SafeHandle atau turunan untuk membungkus handel yang tidak dikelola.

Beberapa hal yang perlu diingat saat menggunakan finalizer:

  • Finalizer tidak dapat ditentukan dalam jenis struct. Mereka hanya digunakan dengan kelas.
  • Kelas hanya dapat memiliki satu finalizer.
  • Finalizer tidak dapat diwariskan atau kelebihan beban.
  • Finalizer tidak dapat dipanggil. Mereka dipanggil secara otomatis.
  • Finalizer tidak mengambil pengubah atau memiliki parameter.

Sintaksis finalizer

Di C#, finalizer didefinisikan menggunakan tilde (~) diikuti dengan nama kelas. Ini tidak mengambil parameter apa pun dan tidak dapat dipanggil secara eksplisit.


class Car
{
    ~Car()  // finalizer
    {
        // cleanup statements...
    }
}

Finalizer juga dapat diimplementasikan sebagai definisi isi ekspresi, seperti yang ditunjukkan contoh berikut.


public class Destroyer
{
   public override string ToString() => GetType().Name;

   ~Destroyer() => Console.WriteLine($"The {ToString()} finalizer is executing.");
}

Hubungan antara pengumpulan sampah dan finalizer

Pengumpul sampah di .NET secara otomatis mengelola alokasi dan pelepasan memori untuk objek terkelola. Ketika objek tidak lagi direferensikan, GC menandainya untuk koleksi dan akhirnya mengklaim kembali memorinya. Jika kelas memiliki finalizer, GC memanggil finalizer sebelum mengklaim ulang memori objek. Finalizer memungkinkan objek untuk merilis sumber daya yang tidak dikelola yang dipegangnya.

Proses finalisasi biasanya melibatkan langkah-langkah berikut:

  • Ketika GC mendeteksi bahwa objek dengan finalizer tidak lagi dapat dijangkau, GC memindahkan objek ke antrean finalisasi.
  • Utas finalizer menjalankan metode finalizer.
  • Setelah finalizer berjalan, objek dipindahkan ke antrean freachable GC, di mana memenuhi syarat untuk pengumpulan sampah di siklus GC berikutnya.

Contoh finalizer

public class ResourceHolder
{
    // Unmanaged resource
    private IntPtr unmanagedResource;

    // Constructor
    public ResourceHolder()
    {
        // Allocate unmanaged resource
        unmanagedResource = /* allocate resource */;
    }

    // Finalizer
    ~ResourceHolder()
    {
        // Release unmanaged resource
        if (unmanagedResource != IntPtr.Zero)
        {
            // Free the resource
            /* free resource */
            unmanagedResource = IntPtr.Zero;
        }
    }
}

Menerapkan pola pembuangan dengan finalizer

Antarmuka seperti IDisposable dan IAsyncDisposable digunakan untuk merilis sumber daya secara deterministik. Metode Dispose dipanggil secara eksplisit untuk merilis sumber daya, sementara finalizer bertindak sebagai jaring pengaman untuk memastikan bahwa sumber daya dirilis bahkan jika Dispose tidak dipanggil.

Penting

Membuat dan menggunakan antarmuka berada di luar cakupan modul ini. Penggunaan IDisposable dijelaskan di sini untuk memberikan konteks untuk pola "buang". Pelatihan yang menyediakan pengenalan antarmuka tersedia di platform Microsoft Learn.

Ingatlah poin-poin berikut saat menggunakan finalizer:

  • Finalizer bersifat nondeterministik, yang berarti Anda tidak dapat memprediksi kapan finalizer berjalan. Tergantung pada jadwal GC. Perilaku ini dapat menyebabkan keterlambatan dalam merilis sumber daya yang tidak dikelola.
  • Finalizer dapat memengaruhi performa karena objek dengan finalizer membutuhkan waktu lebih lama untuk dikumpulkan.
  • Menerapkan antarmuka IDisposable dan metode Dispose untuk pembersihan sumber daya deterministik disarankan. Metode Dispose dapat dipanggil secara eksplisit untuk melepaskan sumber daya, dan finalizer dapat digunakan sebagai jaring pengaman.

Berikut adalah contoh kelas yang mengimplementasikan antarmuka IDisposable dan finalizer:

public class ResourceHolder : IDisposable
{
    // Unmanaged resource
    private IntPtr unmanagedResource;
    private bool disposed = false;

    // Constructor
    public ResourceHolder()
    {
        // Allocate unmanaged resource
        unmanagedResource = /* allocate resource */;
    }

    // Dispose method
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Free other managed objects
            }

            // Free unmanaged resources
            if (unmanagedResource != IntPtr.Zero)
            {
                /* free resource */
                unmanagedResource = IntPtr.Zero;
            }

            disposed = true;
        }
    }

    // Finalizer
    ~ResourceHolder()
    {
        Dispose(false);
    }
}

Dalam contoh ini:

  • Metode Dispose dipanggil untuk merilis sumber daya secara deterministik.
  • Finalizer memanggil Dispose(false) untuk merilis sumber daya yang tidak dikelola jika Dispose tidak dipanggil.
  • GC.SuppressFinalize(this) digunakan untuk mencegah finalizer berjalan jika Dispose telah dipanggil.