Menerapkan metode Dispose
Metode Dispose ini terutama diimplementasikan untuk merilis sumber daya yang tidak dikelola. Saat bekerja dengan anggota instans yang merupakan implementasi IDisposable, panggilan Dispose berjenjang merupakan hal yang umum. 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 melepaskan memori yang tidak dikelola. Pola untuk membuang objek, yang disebut sebagai pola buang, memberlakukan urutan pada masa pakai objek. Pola pembuangan digunakan untuk objek yang mengimplementasikan IDisposable antarmuka. Pola ini umum terjadi saat berinteraksi dengan handel file dan pipa, handel registri, handel tunggu, atau pointer ke blok memori yang tidak dikelola, karena pengumpul sampah tidak dapat mengklaim kembali objek yang tidak dikelola.
Untuk membantu memastikan bahwa sumber daya selalu dibersihkan dengan tepat, Dispose metode harus idempoten, sehingga dapat dipanggil beberapa kali tanpa melemparkan pengecualian. Selanjutnya, pemanggilan Dispose berikutnya tidak boleh melakukan apa-apa.
Contoh kode yang disediakan untuk metode menunjukkan GC.KeepAlive bagaimana pengumpulan sampah dapat menyebabkan finalizer berjalan saat referensi yang tidak dikelola 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.
Tip
Berkenaan dengan injeksi dependensi, saat mendaftarkan layanan di IServiceCollection, masa hidup layanan dikelola secara implisit atas nama Anda. IServiceProvider dan IHost yang sesuai mengatur pembersihan sumber daya. Secara khusus, implementasi IDisposable dan IAsyncDisposable dibuang dengan benar pada akhir masa pakai yang ditentukan.
Untuk informasi selengkapnya, lihat Injeksi dependensi.
Handel 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 System.Runtime.InteropServices.SafeHandle objek dan bukan menerapkan finalizer.
System.Runtime.InteropServices.SafeHandle adalah jenis terkelola abstrak yang membungkus System.IntPtr yang mengidentifikasi sumber daya yang tidak dikelola. Pada Windows mungkin mengidentifikasi handel, dan di Unix, pendeskripsi file. SafeHandle
menyediakan semua logika yang diperlukan untuk memastikan bahwa sumber daya ini dirilis sekali dan hanya sekali, baik ketika SafeHandle
dibuang dari atau ketika semua referensi ke SafeHandle
telah dihilangkan dan SafeHandle
instans diselesaikan.
System.Runtime.InteropServices.SafeHandle adalah kelas dasar abstrak. Kelas turunan menyediakan instans tertentu untuk berbagai jenis handel. Kelas turunan ini memvalidasi nilai apa untuk System.IntPtr yang dianggap tidak valid dan bagaimana sebenarnya membebaskan pegangannya. Misalnya, SafeFileHandle berasal dari SafeHandle
untuk membungkus IntPtrs
yang mengidentifikasi pegangan/deskriptor file yang terbuka, dan menggantikan metode SafeHandle.ReleaseHandle() untuk menutupnya (melalui fungsi close
di Unix atau fungsi CloseHandle
di Windows). Sebagian besar API di pustaka .NET yang membuat sumber daya yang tidak dikelola membungkusnya dalam SafeHandle
dan mengembalikannya SafeHandle
sesuai kebutuhan, 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 jenis SafeHandle
Anda sendiri untuk membungkusnya. Akibatnya, beberapa non-jenisSafeHandle
perlu menerapkan finalizer. Sebagian besar implementasi pola sekali pakai hanya akhirnya membungkus sumber daya terkelola lainnya, beberapa di antaranya mungkin objek SafeHandle
.
Kelas turunan berikut di Microsoft.Win32.SafeHandles namespace layanan menyediakan handel yang aman.
Kelas | Sumber daya yang dipegangnya |
---|---|
SafeFileHandle SafeMemoryMappedFileHandle SafePipeHandle |
File, file yang dipetakan memori, dan pipa |
SafeMemoryMappedViewHandle | Tampilan memori |
SafeNCryptKeyHandle SafeNCryptProviderHandle SafeNCryptSecretHandle |
Konstruksi kriptografi |
SafeRegistryHandle | Kunci registri |
SafeWaitHandle | Handel tunggu |
Dispose() dan Dispose(bool)
Antarmuka IDisposable memerlukan implementasi metode tanpa parameter tunggal, Dispose. Selain itu, kelas yang tidak disegel harus memiliki Dispose(bool)
metode kelebihan beban.
Tanda tangan metode adalah:
public
nonvirtual (NotOverridable
dalam Visual Basic) (IDisposable.Dispose implementasi).protected virtual
(Overridable
dalam Visual Basic)Dispose(bool)
.
Metode Dispose()
public
Karena , non-virtual (NotOverridable
dalam Visual Basic), metode tanpa Dispose
parameter dipanggil ketika tidak lagi diperlukan (oleh konsumen jenis), tujuannya adalah untuk membebaskan sumber daya yang tidak dikelola, melakukan pembersihan umum, dan untuk menunjukkan bahwa finalizer, jika ada, tidak perlu dijalankan. Membebaskan memori sebenarnya yang terkait dengan objek terkelola selalu menjadi domain 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 perlu lagi memanggil penggantian Object.Finalize objek. Oleh karena itu, panggilan ke metode SuppressFinalize mencegah pengumpul sampah menjalankan finalizer. Jika jenis tidak memiliki finalizer, panggilan ke GC.SuppressFinalize tidak berpengaruh. Pembersihan aktual dilakukan oleh Dispose(bool)
metode kelebihan beban.
Overload metode Dispose(bool)
Dalam overload, parameter disposing
adalah Boolean yang menunjukkan apakah pemanggilan metode berasal dari metode Dispose (nilainya true
) atau dari finalizer (nilainya false
).
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
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.
_disposed = true;
}
Protected Overridable Sub Dispose(disposing As Boolean)
If disposed Then Exit Sub
' A block that frees unmanaged resources.
If disposing Then
' Deterministic call…
' A conditional block that frees managed resources.
End If
disposed = True
End Sub
Penting
Parameter disposing
harus false
saat dipanggil dari finalizer, dan true
saat dipanggil dari metode IDisposable.Dispose. Dengan kata lain, ini adalah true
ketika dipanggil secara deterministik dan false
ketika dipanggil secara nondeterministik.
Isi metode terdiri dari tiga blok kode:
Blok untuk pengembalian kondisional jika objek sudah dibuang.
Blok yang membebaskan sumber daya yang tidak terkelola. Blok ini dijalankan terlepas dari nilai parameter
disposing
.Blok bersyarat yang membebaskan sumber daya terkelola. 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 (cascade dispose). 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 menghabiskan banyak memori atau menghabiskan 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 nondeterministik.
Jika panggilan metode berasal dari finalizer, hanya kode yang membebaskan sumber daya yang tidak terkelola yang harus dijalankan. Pelaksana bertanggung jawab untuk memastikan bahwa jalur palsu tidak berinteraksi dengan objek terkelola yang mungkin telah dibuang. Ini penting karena urutan pengumpul sampah membuang objek terkelola selama finalisasi tidak deterministik.
Panggilan buang kaskade
Jika kelas Anda memiliki bidang atau properti dan jenisnya mengimplementasikan IDisposable, kelas yang berisi itu sendiri juga harus menerapkan IDisposable. Kelas yang membuat instans IDisposable implementasi dan menyimpannya sebagai anggota instans juga bertanggung jawab atas pembersihannya. Ini membantu memastikan bahwa jenis sekali pakai yang dirujuk diberi kesempatan untuk melakukan pembersihan Dispose secara deterministik melalui metode . Dalam contoh berikut, kelasnya adalah 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
Tip
- Jika kelas Anda memiliki bidang atau properti tetapi tidakmemilikinyaIDisposable, yang berarti kelas tidak membuat objek, maka kelas tidak perlu menerapkan IDisposable.
- Ada kasus ketika Anda mungkin ingin melakukan
null
-checking di finalizer (yang mencakupDispose(false)
metode yang dipanggil oleh finalizer). Salah satu alasan utamanya adalah jika Anda tidak yakin apakah instans diinisialisasi sepenuhnya (misalnya, pengecualian mungkin dilemparkan ke dalam konstruktor).
Menerapkan pola pembuangan
Semua kelas yang tidak disegel (atau kelas Visual Basic tidak dimodifikasi sebagai NotInheritable
) harus dianggap sebagai kelas dasar potensial, karena dapat diwariskan. Jika Anda menerapkan pola buang untuk kelas dasar potensial, Anda harus memberikan hal berikut:
- Implementasi Dispose yang memanggil metode
Dispose(bool)
. - Metode
Dispose(bool)
yang melakukan pembersihan aktual. - Baik kelas yang berasal dari SafeHandle yang membungkus sumber daya Anda yang tidak terkelola (disarankan), atau penimpaan ke metode Object.Finalize. Kelas ini SafeHandle menyediakan finalizer, jadi Anda tidak perlu menulisnya sendiri.
Penting
Dimungkinkan bagi kelas dasar untuk hanya mereferensikan objek terkelola dan menerapkan pola pembuangan. Dalam kasus ini, finalizer tidak perlu. Finalizer hanya diperlukan jika Anda langsung mereferensikan sumber daya yang tidak dikelola.
Berikut adalah contoh umum penerapan pola pembuangan untuk kelas dasar yang menggunakan handel yang aman.
using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;
public class BaseClassWithSafeHandle : IDisposable
{
// To detect redundant calls
private bool _disposedValue;
// Instantiate a SafeHandle instance.
private SafeHandle? _safeHandle = new SafeFileHandle(IntPtr.Zero, true);
// 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 (!_disposedValue)
{
if (disposing)
{
_safeHandle?.Dispose();
_safeHandle = null;
}
_disposedValue = true;
}
}
}
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.InteropServices
Public Class BaseClassWithSafeHandle
Implements IDisposable
' To detect redundant calls
Private _disposedValue As Boolean
' Instantiate a SafeHandle instance.
Private _safeHandle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)
' 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(ByVal disposing As Boolean)
If Not _disposedValue Then
If disposing Then
_safeHandle?.Dispose()
_safeHandle = Nothing
End If
_disposedValue = True
End If
End Sub
End Class
Catatan
Contoh sebelumnya menggunakan objek SafeFileHandle untuk mengilustrasikan pola; objek apa pun yang berasal dari SafeHandle dapat digunakan sebagai gantinya. Perhatikan bahwa contoh tidak membuat instans objek SafeFileHandle dengan benar.
Berikut adalah pola umum untuk menerapkan pola pembuangan untuk kelas dasar yang mengambil alih Object.Finalize.
using System;
public class BaseClassWithFinalizer : IDisposable
{
// To detect redundant calls
private bool _disposedValue;
~BaseClassWithFinalizer() => 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)
{
if (!_disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects)
}
// TODO: free unmanaged resources (unmanaged objects) and override finalizer
// TODO: set large fields to null
_disposedValue = true;
}
}
}
Public Class BaseClassWithFinalizer
Implements IDisposable
' To detect redundant calls
Private _disposedValue As Boolean
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(ByVal disposing As Boolean)
If Not _disposedValue Then
If disposing Then
' TODO: dispose managed state (managed objects)
End If
' TODO free unmanaged resources (unmanaged objects) And override finalizer
' TODO: set large fields to null
_disposedValue = True
End If
End Sub
End Class
Tip
Dalam 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 diturunkan dari kelas yang mengimplementasikan antarmuka IDisposable tidak boleh mengimplementasikan IDisposable, karena implementasi kelas dasar IDisposable.Dispose diwarisi oleh kelas turunannya. Sebagai gantinya, untuk membersihkan kelas turunan, Anda memberikan hal berikut:
- Metode
protected override void Dispose(bool)
yang mengambil alih metode kelas dasar dan melakukan pembersihan aktual dari kelas turunan. Metode ini juga harus memanggil metodebase.Dispose(bool)
(MyBase.Dispose(bool)
dalam Visual Basic) dengan meneruskan status pembuangan (parameterbool disposing
) sebagai argumen. - Baik kelas yang berasal dari SafeHandle yang membungkus sumber daya Anda yang tidak terkelola (disarankan), atau penimpaan ke metode Object.Finalize. Kelas SafeHandle ini menyediakan finalizer yang membebaskan Anda dari harus membuat kode. Jika Anda menyediakan finalizer, finalizer ini harus memanggil overload
Dispose(bool)
dengan argumenfalse
.
Berikut adalah contoh pola umum untuk menerapkan pola pembuangan untuk kelas turunan yang menggunakan handel yang aman:
using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;
public class DerivedClassWithSafeHandle : BaseClassWithSafeHandle
{
// To detect redundant calls
private bool _disposedValue;
// Instantiate a SafeHandle instance.
private SafeHandle? _safeHandle = new SafeFileHandle(IntPtr.Zero, true);
// Protected implementation of Dispose pattern.
protected override void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
_safeHandle?.Dispose();
_safeHandle = null;
}
_disposedValue = true;
}
// Call base class implementation.
base.Dispose(disposing);
}
}
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.InteropServices
Public Class DerivedClassWithSafeHandle
Inherits BaseClassWithSafeHandle
' To detect redundant calls
Private _disposedValue As Boolean
' Instantiate a SafeHandle instance.
Private _safeHandle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If Not _disposedValue Then
If disposing Then
_safeHandle?.Dispose()
_safeHandle = Nothing
End If
_disposedValue = True
End If
' Call base class implementation.
MyBase.Dispose(disposing)
End Sub
End Class
Catatan
Contoh sebelumnya menggunakan objek SafeFileHandle untuk mengilustrasikan pola; objek apa pun yang berasal dari SafeHandle dapat digunakan sebagai gantinya. Perhatikan bahwa contoh tidak membuat instans objek SafeFileHandle dengan benar.
Berikut adalah pola umum untuk menerapkan pola pembuangan untuk kelas turunan yang mengambil alih Object.Finalize:
public class DerivedClassWithFinalizer : BaseClassWithFinalizer
{
// To detect redundant calls
private bool _disposedValue;
~DerivedClassWithFinalizer() => Dispose(false);
// Protected implementation of Dispose pattern.
protected override void Dispose(bool disposing)
{
if (!_disposedValue)
{
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.
_disposedValue = true;
}
// Call the base class implementation.
base.Dispose(disposing);
}
}
Public Class DerivedClassWithFinalizer
Inherits BaseClassWithFinalizer
' To detect redundant calls
Private _disposedValue As Boolean
Protected Overrides Sub Finalize()
Dispose(False)
End Sub
' Protected implementation of Dispose pattern.
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If Not _disposedValue 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.
_disposedValue = True
End If
' Call the base class implementation.
MyBase.Dispose(disposing)
End Sub
End Class