Antarmuka System.IDisposable

Artikel ini menyediakan keterangan tambahan untuk dokumentasi referensi untuk API ini.

Penggunaan utama IDisposable antarmuka adalah untuk merilis sumber daya yang tidak dikelola. Pengumpul sampah secara otomatis melepaskan memori yang dialokasikan ke objek terkelola ketika objek tersebut tidak lagi digunakan. Namun, tidak mungkin untuk memprediksi kapan pengumpulan sampah akan terjadi. Selain itu, pengumpul sampah tidak memiliki pengetahuan tentang sumber daya yang tidak dikelola seperti handel jendela, atau membuka file dan aliran.

Dispose Gunakan metode antarmuka ini untuk secara eksplisit merilis sumber daya yang tidak dikelola bersama dengan pengumpul sampah. Konsumen objek dapat memanggil metode ini ketika objek tidak lagi diperlukan.

Peringatan

Ini adalah perubahan yang melanggar untuk menambahkan IDisposable antarmuka ke kelas yang ada. Karena konsumen yang sudah ada sebelumnya dari jenis Anda tidak dapat memanggil Dispose, Anda tidak dapat yakin bahwa sumber daya yang tidak dikelola yang dipegang oleh jenis Anda akan dirilis.

IDisposable.Dispose Karena implementasi dipanggil oleh konsumen jenis ketika sumber daya yang dimiliki oleh instans tidak lagi diperlukan, Anda harus membungkus objek terkelola dalam SafeHandle (alternatif yang direkomendasikan), atau Anda harus mengambil alih Object.Finalize untuk membebaskan sumber daya yang tidak dikelola jika konsumen lupa memanggil Dispose.

Penting

Dalam .NET Framework, pengkompilasi C++ mendukung pembuangan deterministik sumber daya dan tidak mengizinkan implementasi Dispose langsung metode.

Untuk diskusi terperinci tentang bagaimana antarmuka ini dan Object.Finalize metode ini digunakan, lihat Topik Pengumpulan sampah dan Menerapkan Metode Buang.

Menggunakan objek yang mengimplementasikan IDisposable

Jika aplikasi Anda hanya menggunakan objek yang mengimplementasikan IDisposable antarmuka, Anda harus memanggil implementasi objek IDisposable.Dispose saat Anda selesai menggunakannya. Bergantung pada bahasa pemrograman Anda, Anda dapat melakukan ini dengan salah satu dari dua cara:

  • Dengan menggunakan konstruksi bahasa seperti using pernyataan dalam C# dan Visual Basic, dan use pernyataan atau using fungsi dalam F#.
  • Dengan membungkus panggilan ke IDisposable.Dispose implementasi dalam blok try/finally .

Catatan

Dokumentasi untuk jenis yang menerapkan IDisposable perhatikan bahwa fakta dan menyertakan pengingat untuk memanggil implementasinya Dispose .

Pernyataan C#, F#, dan Visual Basic Menggunakan

Jika bahasa Anda mendukung konstruksi seperti pernyataan penggunaan di C#, pernyataan Penggunaan di Visual Basic, atau pernyataan penggunaan di F#, Anda dapat menggunakannya alih-alih secara eksplisit memanggil IDisposable.Dispose diri Anda sendiri. Contoh berikut menggunakan pendekatan ini dalam menentukan WordCount kelas yang mempertahankan informasi tentang file dan jumlah kata di dalamnya.

using System;
using System.IO;
using System.Text.RegularExpressions;

public class WordCount
{
    private String filename = String.Empty;
    private int nWords = 0;
    private String pattern = @"\b\w+\b";

    public WordCount(string filename)
    {
        if (!File.Exists(filename))
            throw new FileNotFoundException("The file does not exist.");

        this.filename = filename;
        string txt = String.Empty;
        using (StreamReader sr = new StreamReader(filename))
        {
            txt = sr.ReadToEnd();
        }
        nWords = Regex.Matches(txt, pattern).Count;
    }

    public string FullName
    { get { return filename; } }

    public string Name
    { get { return Path.GetFileName(filename); } }

    public int Count
    { get { return nWords; } }
}
open System.IO
open System.Text.RegularExpressions

type WordCount(filename) =
    let txt = 
        if File.Exists filename |> not then
            raise (FileNotFoundException "The file does not exist.")

        use sr = new StreamReader(filename)
        sr.ReadToEnd()

    let pattern = @"\b\w+\b"
    
    let nWords = Regex.Matches(txt, pattern).Count

    member _.FullName = filename

    member _.Name = Path.GetFileName filename

    member _.Count = nWords
Imports System.IO
Imports System.Text.RegularExpressions

Public Class WordCount
   Private filename As String
   Private nWords As Integer
   Private pattern As String = "\b\w+\b" 

   Public Sub New(filename As String)
      If Not File.Exists(filename) Then
         Throw New FileNotFoundException("The file does not exist.")
      End If   
      
      Me.filename = filename
      Dim txt As String = String.Empty
      Using sr As New StreamReader(filename)
         txt = sr.ReadToEnd()
      End Using
      nWords = Regex.Matches(txt, pattern).Count
   End Sub
   
   Public ReadOnly Property FullName As String
      Get
         Return filename
      End Get   
   End Property
   
   Public ReadOnly Property Name As String
      Get
         Return Path.GetFileName(filename)
      End Get   
   End Property
   
   Public ReadOnly Property Count As Integer
      Get
         Return nWords
      End Get
   End Property
End Class

Pernyataan using (use ekspresi dalam F#) sebenarnya adalah kenyamanan sinaksis. Pada waktu kompilasi, pengkompilasi bahasa mengimplementasikan bahasa perantara (IL) untuk blok try/finally .

Untuk informasi selengkapnya tentang pernyataan, using lihat menggunakan Pernyataan atau menggunakan topik Pernyataan .

Blok Coba/Akhirnya

Jika bahasa pemrograman Anda tidak mendukung konstruksi seperti using pernyataan di C# atau Visual Basic, atau use pernyataan di F#, atau jika Anda lebih suka tidak menggunakannya, Anda dapat memanggil IDisposable.Dispose implementasi dari finally blok try/finally pernyataan. Contoh berikut mengganti blok using dalam contoh sebelumnya dengan try/finally blok.

using System;
using System.IO;
using System.Text.RegularExpressions;

public class WordCount2
{
    private String filename = String.Empty;
    private int nWords = 0;
    private String pattern = @"\b\w+\b";

    public WordCount2(string filename)
    {
        if (!File.Exists(filename))
            throw new FileNotFoundException("The file does not exist.");

        this.filename = filename;
        string txt = String.Empty;
        StreamReader? sr = null;
        try
        {
            sr = new StreamReader(filename);
            txt = sr.ReadToEnd();
        }
        finally
        {
            if (sr != null) sr.Dispose();
        }
        nWords = Regex.Matches(txt, pattern).Count;
    }

    public string FullName
    { get { return filename; } }

    public string Name
    { get { return Path.GetFileName(filename); } }

    public int Count
    { get { return nWords; } }
}
open System.IO
open System.Text.RegularExpressions

type WordCount2(filename) =
    let txt = 
        if File.Exists filename |> not then
            raise (FileNotFoundException "The file does not exist.")

        let sr = new StreamReader(filename)
        try
            sr.ReadToEnd()
        finally
            sr.Dispose()

    let pattern = @"\b\w+\b"
    
    let nWords = Regex.Matches(txt, pattern).Count

    member _.FullName = filename

    member _.Name = Path.GetFileName filename

    member _.Count = nWords
Imports System.IO
Imports System.Text.RegularExpressions

Public Class WordCount2
   Private filename As String
   Private nWords As Integer
   Private pattern As String = "\b\w+\b" 

   Public Sub New(filename As String)
      If Not File.Exists(filename) Then
         Throw New FileNotFoundException("The file does not exist.")
      End If   
      
      Me.filename = filename
      Dim txt As String = String.Empty
      Dim sr As StreamReader = Nothing
      Try
         sr = New StreamReader(filename)
         txt = sr.ReadToEnd()
      Finally
         If sr IsNot Nothing Then sr.Dispose() 
      End Try
      nWords = Regex.Matches(txt, pattern).Count
   End Sub
   
   Public ReadOnly Property FullName As String
      Get
         Return filename
      End Get   
   End Property
   
   Public ReadOnly Property Name As String
      Get
         Return Path.GetFileName(filename)
      End Get   
   End Property
   
   Public ReadOnly Property Count As Integer
      Get
         Return nWords
      End Get
   End Property
End Class

Untuk informasi selengkapnya tentang pola, try/finally lihat Coba... Menangkap... Pernyataan Terakhir, coba akhirnya, coba... akhirnya Ekspresi, atau coba-akhirnya Pernyataan.

Menerapkan IDisposable

Anda harus menerapkan IDisposable jika jenis Anda menggunakan sumber daya yang tidak dikelola secara langsung atau jika Anda ingin menggunakan sumber daya sekali pakai sendiri. Konsumen jenis Anda dapat memanggil implementasi Anda IDisposable.Dispose ke sumber daya gratis ketika instans tidak lagi diperlukan. Untuk menangani kasus di mana mereka gagal memanggil Dispose, Anda harus menggunakan kelas yang berasal dari SafeHandle untuk membungkus sumber daya yang tidak dikelola, atau Anda harus mengambil alih Object.Finalize metode untuk jenis referensi. Dalam kedua kasus, Anda menggunakan Dispose metode untuk melakukan pembersihan apa pun yang diperlukan setelah menggunakan sumber daya yang tidak dikelola, seperti membebaskan, melepaskan, atau mengatur ulang sumber daya yang tidak dikelola. Untuk informasi selengkapnya tentang menerapkan IDisposable.Dispose, lihat metode Buang (bool) kelebihan beban.

Penting

Jika Anda mendefinisikan kelas dasar yang menggunakan sumber daya yang tidak dikelola dan yang memiliki, atau kemungkinan memiliki, subkelas yang harus dibuang, Anda harus menerapkan IDisposable.Dispose metode dan memberikan kelebihan kedua dari Dispose, seperti yang dibahas di bagian berikutnya.

IDisposable dan hierarki warisan

Kelas dasar dengan subkelas yang harus sekali pakai harus diimplementasikan IDisposable sebagai berikut. Anda harus menggunakan pola ini setiap kali Anda menerapkan IDisposable pada jenis apa pun yang tidak sealed (NotInheritable di Visual Basic).

  • Ini harus menyediakan satu metode publik, non-virtual Dispose() dan metode virtual Dispose(Boolean disposing) yang dilindungi.
  • Metode Dispose() harus memanggil Dispose(true) dan harus menekan finalisasi untuk performa.
  • Jenis dasar tidak boleh menyertakan finalizer apa pun.

Fragmen kode berikut mencerminkan pola buang untuk kelas dasar. Ini mengasumsikan bahwa jenis Anda tidak mengambil Object.Finalize alih metode .

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

class BaseClass1 : IDisposable
{
    // Flag: Has Dispose already been called?
    bool disposed = false;
    // Instantiate a FileStream instance.
    FileStream fs = new FileStream("test.txt", FileMode.OpenOrCreate);

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

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (disposed)
            return;

        if (disposing)
        {
            fs.Dispose();
            // Free any other managed objects here.
            //
        }

        disposed = true;
    }
}
open System
open System.IO

type BaseClass1() =
    // Flag: Has Dispose already been called?
    let mutable disposed = false

    // Instantiate a FileStream instance.
    let fs = new FileStream("test.txt", FileMode.OpenOrCreate)

    interface IDisposable with
        // Public implementation of Dispose pattern callable by consumers.
        member this.Dispose() =
            this.Dispose true
            GC.SuppressFinalize this

    // Implementation of Dispose pattern.
    abstract Dispose: bool -> unit
    override _.Dispose(disposing) =
        if not disposed then
            if disposing then
                fs.Dispose()
                // Free any other managed objects here.
            disposed <- true
Imports System.IO
Imports System.Runtime.InteropServices

Class BaseClass1 : Implements IDisposable
   ' Flag: Has Dispose already been called?
   Dim disposed As Boolean = False
   ' Instantiate a FileStream instance.
   Dim fs As FileStream = New FileStream("test.txt", FileMode.OpenOrCreate)

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

   ' Protected implementation of Dispose pattern.
   Protected Overridable Sub Dispose(disposing As Boolean)
      If disposed Then Return

      If disposing Then
         fs.Dispose()
         ' Free any other managed objects here.
         '
      End If

      disposed = True
   End Sub
End Class

Jika Anda mengambil alih metode , Object.Finalize kelas Anda harus menerapkan pola berikut.

using System;

class BaseClass2 : IDisposable
{
    // Flag: Has Dispose already been called?
    bool disposed = false;

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

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (disposed)
            return;

        if (disposing)
        {
            // Free any other managed objects here.
            //
        }

        // Free any unmanaged objects here.
        //
        disposed = true;
    }

    ~BaseClass2()
    {
        Dispose(disposing: false);
    }
}
open System

type BaseClass2() =
    // Flag: Has Dispose already been called?
    let mutable disposed = false

    interface IDisposable with
        // Public implementation of Dispose pattern callable by consumers.
        member this.Dispose() =
            this.Dispose true
            GC.SuppressFinalize this

    // Implementation of Dispose pattern.
    abstract Dispose: bool -> unit
    override _.Dispose(disposing) =
        if not disposed then
            if disposing then
                // Free any other managed objects here.
                ()

            // Free any unmanaged objects here.
            disposed <- true

    override this.Finalize() =
        this.Dispose false
Class BaseClass : Implements IDisposable
   ' Flag: Has Dispose already been called?
   Dim disposed As Boolean = False

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

   ' Protected implementation of Dispose pattern.
   Protected Overridable Sub Dispose(disposing As Boolean)
      If disposed Then Return

      If disposing Then
         ' Free any other managed objects here.
         '
      End If

      ' Free any unmanaged objects here.
      '
      disposed = True
   End Sub

   Protected Overrides Sub Finalize()
      Dispose(disposing:=False)
   End Sub
End Class

Subkelas harus menerapkan pola sekali pakai sebagai berikut:

  • Mereka harus mengambil alih Dispose(Boolean) dan memanggil implementasi kelas Dispose(Boolean) dasar.
  • Mereka dapat menyediakan finalizer jika diperlukan. Finalizer harus memanggil Dispose(false).

Perhatikan bahwa kelas turunan tidak mengimplementasikan IDisposable antarmuka itu sendiri dan tidak menyertakan metode tanpa Dispose parameter. Mereka hanya mengambil alih metode kelas Dispose(Boolean) dasar.

Fragmen kode berikut mencerminkan pola pembuangan untuk kelas turunan. Ini mengasumsikan bahwa jenis Anda tidak mengambil Object.Finalize alih metode .

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

class MyDerivedClass : MyBaseClass
{
    // Flag: Has Dispose already been called?
    bool disposed = false;
    // Instantiate a FileStream instance.
    FileStream fs = new FileStream("test.txt", FileMode.OpenOrCreate);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (disposed)
            return;

        if (disposing)
        {
            fs.Dispose();
            // Free any other managed objects here.
            //
        }

        // Free any unmanaged objects here.
        //

        disposed = true;
        // Call base class implementation.
        base.Dispose(disposing);
    }
}
open Microsoft.Win32.SafeHandles
open System

type MyDerivedClass() =
    inherit MyBaseClass()
    
    // Flag: Has Dispose already been called?
    let mutable disposed = false
    // Instantiate a FileStream instance.
    let fs = new FileStream("test.txt", FileMode.OpenOrCreate)

    // Implementation of Dispose pattern.
    override _.Dispose(disposing) =
        if not disposed then
            if disposing then
                fs.Dispose()
                // Free any other managed objects here.

            // Free any unmanaged objects here.
            disposed <- true
            // Call base class implementation.
            base.Dispose disposing
Imports System.IO
Imports System.Runtime.InteropServices

Class DerivedClass2 : Inherits BaseClass2
   ' Flag: Has Dispose already been called?
   Dim disposed As Boolean = False
   ' Instantiate a FileStream instance.
   Dim fs As FileStream = New FileStream("test.txt", FileMode.OpenOrCreate)

   ' Protected implementation of Dispose pattern.
   Protected Overrides Sub Dispose(disposing As Boolean)
      If disposed Then Return

      If disposing Then
         fs.Dispose()
         ' Free any other managed objects here.
         '
      End If

      ' Free any unmanaged objects here.
      '
      disposed = True

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