Bagikan melalui


Menerapkan metode Buang

Metode Dispose terutama diimplementasikan untuk merilis sumber daya yang tidak dikelola. Saat bekerja dengan anggota instans yang merupakan implementasi IDisposable, sering kali dilakukan panggilan Dispose secara berurutan. Ada alasan lain untuk menerapkan Dispose, misalnya, untuk membebaskan memori yang dialokasikan, menghapus item yang ditambahkan ke koleksi, atau memberi sinyal pelepasan kunci yang diperoleh.

Pengumpul sampah .NET tidak mengalokasikan atau merilis memori yang tidak dikelola. Pola untuk membuang objek, disebut sebagai pola membuang, memberlakukan urutan pada masa pakai objek. Pola dispose digunakan untuk objek yang mengimplementasikan antarmuka IDisposable. Pola ini umum terjadi saat berinteraksi dengan pegangan file dan pipa, pegangan registri, pegangan tunggu, atau penunjuk ke blok memori yang tidak dikelola, karena pengumpul sampah tidak dapat mengambil kembali objek yang tidak dikelola.

Untuk membantu memastikan bahwa sumber daya selalu dibersihkan dengan tepat, metode Dispose harus idempoten, sehingga dapat dipanggil beberapa kali tanpa melemparkan pengecualian. Selain itu, pemanggilan berikutnya dari Dispose tidak boleh melakukan apa-apa.

Contoh kode yang disediakan untuk metode GC.KeepAlive menunjukkan bagaimana pengumpulan sampah dapat menyebabkan finalizer dieksekusi saat referensi yang tidak dikelola secara otomatis ke objek atau anggotanya masih digunakan. Mungkin masuk akal untuk menggunakan GC.KeepAlive untuk membuat objek tidak memenuhi syarat untuk pengumpulan sampah dari awal rutinitas saat ini ke titik di mana metode ini dipanggil.

Tips

Sehubungan dengan injeksi dependensi, saat mendaftarkan layanan dalam IServiceCollection, masa pakai layanan dikelola secara implisit atas nama Anda. IServiceProvider dan IHost yang terkait mengoordinasikan pembersihan sumber daya. Secara khusus, implementasi IDisposable dan IAsyncDisposable dibuang dengan benar pada akhir masa pakai yang ditentukan.

Untuk informasi selengkapnya, lihat Injeksi Dependensi di .NET.

Panggilan pembuangan kaskade

Jika kelas Anda memiliki instans jenis lain yang mengimplementasikan IDisposable, kelas yang berisi itu sendiri juga harus menerapkan IDisposable. Biasanya, kelas yang menginstansiasi implementasi IDisposable dan menyimpannya sebagai anggota instans (atau properti) juga bertanggung jawab untuk membersihkannya. Ini membantu memastikan bahwa jenis sekali pakai yang dirujuk memiliki kesempatan untuk melakukan pembersihan secara deterministik melalui metode Dispose. Dalam contoh berikut, kelas sealed (atau NotInheritable di Visual Basic).

using System;

public sealed class Foo : IDisposable
{
    private readonly IDisposable _bar;

    public Foo()
    {
        _bar = new Bar();
    }

    public void Dispose() => _bar.Dispose();
}
Public NotInheritable Class Foo
    Implements IDisposable

    Private ReadOnly _bar As IDisposable

    Public Sub New()
        _bar = New Bar()
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        _bar.Dispose()
    End Sub
End Class

Tips

  • Jika kelas Anda memiliki bidang atau properti IDisposable tetapi tidak memilikinya , maka kelas tersebut tidak perlu menerapkan IDisposable. Biasanya kelas yang membuat dan menyimpan objek anak IDisposable juga menjadi pemilik, tetapi dalam beberapa kasus kepemilikan dapat ditransfer ke jenis IDisposable lain.
  • Ada kasus ketika Anda mungkin ingin melakukan pemeriksaan nulldalam finalisasi (yang mencakup metode Dispose(false) yang dipanggil oleh finalisasi). Salah satu alasan utamanya adalah apabila Anda tidak yakin apakah instans tersebut sudah sepenuhnya diinisialisasi (misalnya, mungkin ada pengecualian yang dilemparkan di dalam konstruktor).

Dispose() dan Dispose(bool)

Antarmuka IDisposable memerlukan implementasi metode tanpa parameter tunggal, Dispose. Selain itu, kelas yang tidak disegel harus memiliki metode overload Dispose(bool).

Signatur metode adalah:

  • public non-virtual (NotOverridable di Visual Basic) ( implementasiIDisposable.Dispose).
  • protected virtual (Overridable di Visual Basic) Dispose(bool).

Metode Dispose()

Karena metode Dispose tanpa parameter, yang bersifat non-virtual (NotOverridable di Visual Basic), dipanggil ketika objek tersebut tidak lagi diperlukan oleh konsumennya, tujuannya adalah untuk membebaskan sumber daya yang tidak dikelola, melakukan pembersihan secara umum, dan memastikan bahwa finalizer, jika ada, tidak perlu dijalankan. Pembebasan memori aktual yang terkait dengan objek terkelola selalu merupakan tanggung jawab pengumpul sampah . Karena itu, ia memiliki implementasi standar:

public void Dispose()
{
    // Dispose of unmanaged resources.
    Dispose(true);
    // Suppress finalization.
    GC.SuppressFinalize(this);
}
Public Sub Dispose() _
    Implements IDisposable.Dispose
    ' Dispose of unmanaged resources.
    Dispose(True)
    ' Suppress finalization.
    GC.SuppressFinalize(Me)
End Sub

Metode Dispose melakukan semua pembersihan objek, sehingga pengumpul sampah tidak lagi diperlukan untuk memanggil metode override Object.Finalize pada objek. Oleh karena itu, panggilan ke metode SuppressFinalize mencegah pengumpul sampah menjalankan finalizer. Jika tipe tidak memiliki finalizer, panggilan ke GC.SuppressFinalize tidak memiliki pengaruh. Pembersihan aktual dilakukan oleh metode Dispose(bool) kelebihan beban.

Metode Buang (bool) kelebihan beban

Dalam kelebihan beban, parameter disposing adalah Boolean yang menunjukkan apakah panggilan metode berasal dari metode Dispose (nilainya true) atau dari finalizer (nilainya false).

protected virtual void Dispose(bool disposing)
{
    if (_disposed)
    {
        return;
    }

    if (disposing)
    {
        // Dispose managed state (managed objects).
        // ...
    }

    // Free unmanaged resources.
    // ...

    _disposed = true;
}
Protected Overridable Sub Dispose(disposing As Boolean)
     If disposed Then Exit Sub

     If disposing Then

         ' Free managed resources.
         ' ...

     End If

     ' Free unmanaged resources.
     ' ...

     disposed = True
End Sub

Penting

Parameter disposing harus false ketika dipanggil dari finalizer, dan true ketika dipanggil dari metode IDisposable.Dispose. Dengan kata lain, itu true ketika secara deterministik dipanggil dan false ketika secara nondeterministik dipanggil.

Isi metode terdiri dari tiga blok kode:

  • Blok kondisional untuk pengembalian jika objek sudah dibuang.

  • Blok bersyarat yang membebaskan sumber daya yang dikelola. Blok ini dijalankan jika nilai disposing adalah true. Sumber daya terkelola yang gratis dapat mencakup:

    • Objek terkelola yang mengimplementasikan IDisposable. Blok bersyarat dapat digunakan untuk memanggil implementasi Dispose mereka (penghapusan berjenjang). Jika Anda telah menggunakan kelas turunan System.Runtime.InteropServices.SafeHandle untuk membungkus sumber daya yang tidak dikelola, Anda harus memanggil implementasi SafeHandle.Dispose() di sini.
    • Objek terkelola yang mengonsumsi memori dalam jumlah besar atau mengonsumsi sumber daya yang langka. Tetapkan referensi objek terkelola besar ke null untuk membuatnya lebih mungkin tidak dapat dijangkau. Ini melepaskannya lebih cepat daripada jika mereka direklamasi secara tidak deterministik.
  • Blok yang membebaskan sumber daya yang tidak dikelola. Blok ini dijalankan terlepas dari nilai parameter disposing.

Jika panggilan metode berasal dari finalizer, hanya kode yang membebaskan sumber daya yang tidak dikelola yang harus dijalankan. Pelaksana bertanggung jawab untuk memastikan bahwa jalur palsu tidak berinteraksi dengan objek terkelola yang mungkin telah dibuang. Ini penting karena pengumpul sampah membuang objek terkelola dalam urutan yang tidak deterministik selama finalisasi.

Menerapkan pola pembuangan

Semua kelas yang tidak disegel (atau kelas Visual Basic yang tidak dimodifikasi sebagai NotInheritable) harus dianggap sebagai kelas dasar potensial, karena dapat diwariskan. Jika Anda menerapkan pola pembuangan untuk kelas dasar potensial, Anda harus menambahkan metode berikut ke kelas Anda:

  • Implementasi Dispose yang memanggil metode Dispose(bool).
  • Metode Dispose(bool) yang melakukan pembersihan sebenarnya.
  • Jika kelas Anda menangani sumber daya yang tidak dikelola, sediakan penggantian untuk metode Object.Finalize atau membungkus sumber daya yang tidak dikelola dalam SafeHandle.

Penting

Finalizer (penimpaan Object.Finalize) hanya diperlukan jika Anda langsung mereferensikan sumber daya yang tidak dikelola. Ini adalah skenario yang sangat canggih yang biasanya dapat dihindari:

  • Jika kelas Anda hanya mereferensikan objek terkelola, kelas masih dapat menerapkan pola pembuangan. Tidak perlu mengimplementasikan finalizer.
  • Jika Anda perlu menangani sumber daya yang tidak dikelola, kami sangat menyarankan untuk membungkus handle IntPtr yang tidak dikelola ke dalam SafeHandle. SafeHandle menyediakan finalizer sehingga Anda tidak perlu menulisnya sendiri. Untuk informasi selengkapnya, lihat paragraf Pegangan Aman.

Kelas dasar dengan sumber daya terkelola

Berikut adalah contoh umum penerapan pola dispose untuk sebuah kelas dasar yang hanya memiliki sumber daya terkelola.

using System;
using System.IO;

public class DisposableBase : IDisposable
{
    // Detect redundant Dispose() calls.
    private bool _isDisposed;

    // Instantiate a disposable object owned by this class.
    private Stream? _managedResource = new MemoryStream();

    // Public implementation of Dispose pattern callable by consumers.
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (!_isDisposed)
        {
            _isDisposed = true;

            if (disposing)
            {
                // Dispose managed state.
                _managedResource?.Dispose();
                _managedResource = null;
            }
        }
    }
}
Imports System.IO

Public Class DisposableBase
    Implements IDisposable

    ' Detect redundant Dispose() calls.
    Private _isDisposed As Boolean

    ' Instantiate a disposable object owned by this class.
    Private _managedResource As Stream = New MemoryStream()

    ' Public implementation of Dispose pattern callable by consumers.
    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    ' Protected implementation of Dispose pattern.
    Protected Overridable Sub Dispose(disposing As Boolean)
        If Not _isDisposed Then
            _isDisposed = True

            If disposing Then
                ' Dispose managed state.
                _managedResource?.Dispose()
                _managedResource = Nothing
            End If
        End If
    End Sub
End Class

Nota

Contoh sebelumnya menggunakan objek MemoryStream dummyuntuk mengilustrasikan pola. Setiap IDisposable dapat digunakan sebagai gantinya.

Kelas dasar dengan sumber daya yang tidak dikelola

Berikut adalah contoh untuk menerapkan pola dispose pada kelas dasar yang mengoverride Object.Finalize guna membersihkan sumber daya tidak dikelola yang dimilikinya. Contohnya juga menunjukkan cara untuk mengimplementasikan Dispose(bool) dengan cara yang thread-safe. Sinkronisasi mungkin sangat penting saat berhadapan dengan sumber daya yang tidak dikelola dalam aplikasi multi-utas. Seperti disebutkan sebelumnya, ini adalah skenario lanjutan.

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;

public class DisposableBaseWithFinalizer : IDisposable
{
    // Detect redundant Dispose() calls in a thread-safe manner.
    // _isDisposed == 0 means Dispose(bool) has not been called yet.
    // _isDisposed == 1 means Dispose(bool) has been already called.
    private int _isDisposed;

    // Instantiate a disposable object owned by this class.
    private Stream? _managedResource = new MemoryStream();

    // A pointer to 10 bytes allocated on the unmanaged heap.
    private IntPtr _unmanagedResource = Marshal.AllocHGlobal(10);

    ~DisposableBaseWithFinalizer() => Dispose(false);

    // Public implementation of Dispose pattern callable by consumers.
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        // In case _isDisposed is 0, atomically set it to 1.
        // Enter the branch only if the original value is 0.
        if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
        {
            if (disposing)
            {
                _managedResource?.Dispose();
                _managedResource = null;
            }

            Marshal.FreeHGlobal(_unmanagedResource);
        }
    }
}
Imports System
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Threading

Public Class DisposableBaseWithFinalizer
    Implements IDisposable

    ' Detect redundant Dispose() calls in a thread-safe manner.
    ' _isDisposed == 0 means Dispose(bool) has not been called yet.
    ' _isDisposed == 1 means Dispose(bool) has been already called.
    Private _isDisposed As Integer

    ' Instantiate a disposable object owned by this class.
    Private _managedResource As Stream = New MemoryStream()

    ' A pointer to 10 bytes allocated on the unmanaged heap.
    Private _unmanagedResource As IntPtr = Marshal.AllocHGlobal(10)

    Protected Overrides Sub Finalize()
        Dispose(False)
    End Sub

    ' Public implementation of Dispose pattern callable by consumers.
    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    ' Protected implementation of Dispose pattern.
    Protected Overridable Sub Dispose(disposing As Boolean)
        ' In case _isDisposed is 0, atomically set it to 1.
        ' Enter the branch only if the original value is 0.
        If Interlocked.CompareExchange(_isDisposed, 1, 0) = 0 Then
            If disposing Then
                _managedResource?.Dispose()
                _managedResource = Nothing
            End If

            Marshal.FreeHGlobal(_unmanagedResource)
        End If
    End Sub
End Class

Nota

  • Contoh sebelumnya menggunakan AllocHGlobal untuk mengalokasikan 10 byte pada tumpukan yang tidak dikelola di konstruktor dan membebaskan buffer di Dispose(bool) dengan memanggil FreeHGlobal. Ini adalah alokasi fiktif untuk tujuan ilustrasi.
  • Sekali lagi, disarankan untuk menghindari penerapan finalizer. Lihat menerapkan pola pelepasan menggunakan handle aman kustom untuk contoh sebelumnya yang setara dengan mendelegasikan finalisasi dan sinkronisasi non-deterministik ke SafeHandle.

Tips

Di C#, Anda menerapkan finalisasi dengan menyediakan finalizer, bukan dengan mengambil alih Object.Finalize. Di Visual Basic, Anda membuat finalizer dengan Protected Overrides Sub Finalize().

Menerapkan pola pembuangan untuk kelas turunan

Kelas yang berasal dari kelas yang mengimplementasikan antarmuka IDisposable tidak boleh menerapkan IDisposable, karena implementasi kelas dasar IDisposable.Dispose diwariskan oleh kelas turunannya. Sebagai gantinya, untuk membersihkan kelas turunan, Anda memberikan hal berikut:

  • Metode protected override void Dispose(bool) yang menggantikan metode kelas dasar dan melakukan pembersihan yang sesungguhnya dari kelas turunan. Metode ini juga harus memanggil metode base.Dispose(bool) (MyBase.Dispose(bool) dalam Visual Basic) dengan meneruskan status disposing (parameterbool disposing) sebagai argumen.
  • Kelas yang berasal dari SafeHandle, yang membungkus sumber daya Anda yang tidak dikelola (disarankan), atau mengesampingkan metode Object.Finalize. Kelas SafeHandle menyediakan finalizer yang membebaskan Anda dari harus menulis kode untuk itu. Jika Anda memberikan finalizer, maka finalizer tersebut harus memanggil overload fungsi Dispose(bool) dengan argumen false.

Berikut adalah contoh pola umum untuk mengimplementasikan pola dispose untuk kelas turunan yang menggunakan handle yang aman:

using System.IO;

public class DisposableDerived : DisposableBase
{
    // To detect redundant calls
    private bool _isDisposed;

    // Instantiate a disposable object owned by this class.
    private Stream? _managedResource = new MemoryStream();

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (!_isDisposed)
        {
            _isDisposed = true;

            if (disposing)
            {
                _managedResource?.Dispose();
                _managedResource = null;
            }
        }

        // Call base class implementation.
        base.Dispose(disposing);
    }
}
Imports System.IO

Public Class DisposableDerived
    Inherits DisposableBase

    ' To detect redundant calls
    Private _isDisposed As Boolean

    ' Instantiate a disposable object owned by this class.
    Private _managedResource As Stream = New MemoryStream()

    ' Protected implementation of Dispose pattern.
    Protected Overrides Sub Dispose(disposing As Boolean)
        If Not _isDisposed Then
            _isDisposed = True

            If disposing Then
                _managedResource?.Dispose()
                _managedResource = Nothing
            End If
        End If

        ' Call base class implementation.
        MyBase.Dispose(disposing)
    End Sub
End Class

Nota

Contoh sebelumnya menggunakan objek SafeFileHandle untuk mengilustrasikan pola; objek apa pun yang berasal dari SafeHandle dapat digunakan sebagai gantinya. Perhatikan bahwa contoh tidak menginstansiasi objek SafeFileHandle dengan benar.

Berikut adalah pola umum untuk menerapkan pola dispose untuk kelas turunan yang melakukan override Object.Finalize:

using System.Threading;

public class DisposableDerivedWithFinalizer : DisposableBaseWithFinalizer
{
    // Detect redundant Dispose() calls in a thread-safe manner.
    // _isDisposed == 0 means Dispose(bool) has not been called yet.
    // _isDisposed == 1 means Dispose(bool) has been already called.
    private int _isDisposed;

    ~DisposableDerivedWithFinalizer() => Dispose(false);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        // In case _isDisposed is 0, atomically set it to 1.
        // Enter the branch only if the original value is 0.
        if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
        {
            if (disposing)
            {
                // TODO: dispose managed state (managed objects).
            }

            // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
            // TODO: set large fields to null.
        }

        // Call the base class implementation.
        base.Dispose(disposing);
    }
}
Imports System.Threading

Public Class DisposableDerivedWithFinalizer
    Inherits DisposableBaseWithFinalizer

    ' Detect redundant Dispose() calls in a thread-safe manner.
    ' _isDisposed == 0 means Dispose(bool) has not been called yet.
    ' _isDisposed == 1 means Dispose(bool) has been already called.
    Private _isDisposed As Integer

    Protected Overrides Sub Finalize()
        Dispose(False)
    End Sub

    ' Protected implementation of Dispose pattern.
    Protected Overrides Sub Dispose(disposing As Boolean)
        ' In case _isDisposed is 0, atomically set it to 1.
        ' Enter the branch only if the original value is 0.
        If Interlocked.CompareExchange(_isDisposed, 1, 0) = 0 Then
            If disposing Then
                ' TODO: dispose managed state (managed objects).
            End If

            ' TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
            ' TODO: set large fields to null.
        End If

        ' Call the base class implementation.
        MyBase.Dispose(disposing)
    End Sub
End Class

Pegangan aman

Menulis kode untuk finalizer objek adalah tugas kompleks yang dapat menyebabkan masalah jika tidak dilakukan dengan benar. Oleh karena itu, kami sarankan Anda membuat objek System.Runtime.InteropServices.SafeHandle alih-alih menerapkan finalizer.

System.Runtime.InteropServices.SafeHandle adalah jenis abstrak yang terkelola, membungkus System.IntPtr yang mengidentifikasi sumber daya yang tidak dikelola. Pada Windows, ini mungkin mengidentifikasi pegangan, dan di Unix, sebuah deskriptor berkas. SafeHandle menyediakan semua logika yang diperlukan untuk memastikan bahwa sumber daya ini dirilis sekali dan hanya sekali, baik ketika SafeHandle dibuang atau ketika semua referensi ke SafeHandle telah dihilangkan dan instans SafeHandle diselesaikan.

System.Runtime.InteropServices.SafeHandle adalah kelas dasar abstrak. Kelas turunan menyediakan instans khusus untuk berbagai jenis pegangan. Kelas turunan ini memvalidasi nilai mana untuk System.IntPtr yang dianggap tidak valid dan bagaimana secara efektif membebaskan handle. Misalnya, SafeFileHandle berasal dari SafeHandle untuk membungkus IntPtrs yang mengidentifikasi handel dan deskriptor file terbuka, dan mengambil alih metode SafeHandle.ReleaseHandle() untuk menutupnya (melalui fungsi close pada fungsi Unix atau CloseHandle di Windows). Sebagian besar API di pustaka .NET yang membuat sumber daya yang tidak dikelola membungkusnya dalam SafeHandle dan mengembalikan SafeHandle tersebut kepada Anda saat diperlukan, daripada menyerahkan kembali pointer mentah. Dalam situasi di mana Anda berinteraksi dengan komponen yang tidak dikelola dan mendapatkan IntPtr untuk sumber daya yang tidak dikelola, Anda dapat membuat tipe SafeHandle Anda sendiri untuk membungkusnya. Akibatnya, sedikit jenis non-SafeHandle yang perlu menerapkan finalizer. Sebagian besar implementasi pola sekali pakai pada akhirnya hanya membungkus sumber daya terkelola lainnya, yang beberapa di antaranya mungkin merupakan objek SafeHandle.

Menerapkan pola dispose menggunakan handle aman khusus

Kode berikut menunjukkan cara menangani sumber daya yang tidak dikelola dengan menerapkan SafeHandle.

using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

// Wraps the IntPtr allocated by Marshal.AllocHGlobal() into a SafeHandle.
class LocalAllocHandle : SafeHandleZeroOrMinusOneIsInvalid
{
    private LocalAllocHandle() : base(ownsHandle: true) { }

    // No need to implement a finalizer - SafeHandle's finalizer will call ReleaseHandle for you.
    protected override bool ReleaseHandle()
    {
        Marshal.FreeHGlobal(handle);
        return true;
    }

    // Allocate bytes with Marshal.AllocHGlobal() and wrap the result into a SafeHandle.
    public static LocalAllocHandle Allocate(int numberOfBytes)
    {
        IntPtr nativeHandle = Marshal.AllocHGlobal(numberOfBytes);
        LocalAllocHandle safeHandle = new LocalAllocHandle();
        safeHandle.SetHandle(nativeHandle);
        return safeHandle;
    }
}

public class DisposableBaseWithSafeHandle : IDisposable
{
    // Detect redundant Dispose() calls.
    private bool _isDisposed;

    // Managed disposable objects owned by this class
    private LocalAllocHandle? _safeHandle = LocalAllocHandle.Allocate(10);
    private Stream? _otherUnmanagedResource = new MemoryStream();

    // Public implementation of Dispose pattern callable by consumers.
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (!_isDisposed)
        {
            _isDisposed = true;

            if (disposing)
            {
                // Dispose managed state.
                _otherUnmanagedResource?.Dispose();
                _safeHandle?.Dispose();
                _otherUnmanagedResource = null;
                _safeHandle = null;
            }
        }
    }
}
Imports System
Imports System.IO
Imports System.Runtime.InteropServices
Imports Microsoft.Win32.SafeHandles

' Wraps the IntPtr allocated by Marshal.AllocHGlobal() into a SafeHandle.
Public Class LocalAllocHandle
    Inherits SafeHandleZeroOrMinusOneIsInvalid

    Private Sub New()
        MyBase.New(True)
    End Sub

    ' No need to implement a finalizer - SafeHandle's finalizer will call ReleaseHandle for you.
    Protected Overrides Function ReleaseHandle() As Boolean
        Marshal.FreeHGlobal(handle)
        Return True
    End Function

    ' Allocate bytes with Marshal.AllocHGlobal() and wrap the result into a SafeHandle.
    Public Shared Function Allocate(numberOfBytes As Integer) As LocalAllocHandle
        Dim nativeHandle As IntPtr = Marshal.AllocHGlobal(numberOfBytes)
        Dim safeHandle As New LocalAllocHandle()
        safeHandle.SetHandle(nativeHandle)
        Return safeHandle
    End Function
End Class

Public Class DisposableBaseWithSafeHandle
    Implements IDisposable

    ' Detect redundant Dispose() calls.
    Private _isDisposed As Boolean

    ' Managed disposable objects owned by this class
    Private _safeHandle As LocalAllocHandle = LocalAllocHandle.Allocate(10)
    Private _otherUnmanagedResource As Stream = New MemoryStream()

    ' Public implementation of Dispose pattern callable by consumers.
    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    ' Protected implementation of Dispose pattern.
    Protected Overridable Sub Dispose(disposing As Boolean)
        If Not _isDisposed Then
            _isDisposed = True

            If disposing Then
                ' Dispose managed state.
                _otherUnmanagedResource?.Dispose()
                _safeHandle?.Dispose()
                _otherUnmanagedResource = Nothing
                _safeHandle = Nothing
            End If
        End If
    End Sub
End Class

Nota

Perilaku kelas DisposableBaseWithSafeHandle setara dengan perilaku kelas DisposableBaseWithFinalizer dalam contoh sebelumnya, namun pendekatan yang ditunjukkan di sini lebih aman:

  • Tidak perlu menerapkan finalizer, karena SafeHandle akan mengurus finalisasi.
  • Tidak perlu melakukan sinkronisasi untuk menjamin keamanan thread. Meskipun ada kondisi balapan dalam implementasi Dispose dari DisposableBaseWithSafeHandle, SafeHandle menjamin bahwa SafeHandle.ReleaseHandle hanya akan dipanggil sekali.

Pegangan aman bawaan di .NET

Kelas turunan berikut di namespace Microsoft.Win32.SafeHandles menyediakan pegangan aman.

Kelas Sumber daya yang dipegangnya
SafeFileHandle
SafeMemoryMappedFileHandle
SafePipeHandle
File, file yang dipetakan ke memori, dan saluran
SafeMemoryMappedViewHandle Tampilan memori
SafeNCryptKeyHandle
SafeNCryptProviderHandle
SafeNCryptSecretHandle
Konstruksi kriptografi
SafeRegistryHandle Kunci registri
SafeWaitHandle Pegangan tunggu

Lihat juga