Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
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
null
dalam finalisasi (yang mencakup metodeDispose(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
adalahtrue
. 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 metodebase.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 argumenfalse
.
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
dariDisposableBaseWithSafeHandle
, 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 |