Bagikan melalui


Menggunakan objek yang mengimplementasikan IDisposable

Pengumpul sampah (GC) runtime bahasa umum mengklaim kembali memori yang digunakan oleh objek terkelola. Biasanya, jenis yang menggunakan sumber daya yang tidak dikelola mengimplementasikan IDisposable antarmuka atau IAsyncDisposable untuk memungkinkan sumber daya yang tidak dikelola diklaim kembali. Setelah selesai menggunakan objek yang mengimplementasikan IDisposable, Anda memanggil implementasi Dispose atau DisposeAsync dari objek tersebut untuk melakukan pembersihan secara eksplisit. Anda dapat melakukan ini dengan salah satu dari dua cara:

  • Dengan pernyataan atau deklarasi C# using (Using dalam Visual Basic).
  • Dengan menerapkan blok try/finally, dan memanggil metode Dispose atau DisposeAsync pada finally.

Penting

GC tidak membuang objek Anda, karena tidak memiliki pengetahuan tentang IDisposable.Dispose() ataupun IAsyncDisposable.DisposeAsync(). GC hanya mengetahui apakah suatu objek dapat di-finalisasi (yaitu, mendefinisikan metode Object.Finalize()), dan kapan finalizer objek perlu dipanggil. Untuk informasi selengkapnya, lihat Cara kerja finalisasi. Untuk detail tambahan tentang penerapan Dispose dan DisposeAsync, lihat:

Objek yang mengimplementasikan System.IDisposable atau System.IAsyncDisposable harus selalu dibuang dengan benar, terlepas dari cakupan variabel, kecuali dinyatakan secara eksplisit. Jenis yang menentukan finalizer untuk melepaskan sumber daya tidak terkelola biasanya memanggil GC.SuppressFinalize dari Dispose atau implementasi DisposeAsync mereka. Memanggil SuppressFinalize memberi tahu GC bahwa finalizer telah dijalankan dan objek tidak seharusnya menjalani proses finalisasi.

Pernyataan penggunaan

Pernyataanusing dalam C# dan Using pernyataan di Visual Basic menyederhanakan kode yang harus Anda tulis untuk membersihkan objek. Pernyataan using memperoleh satu atau beberapa sumber daya, menjalankan pernyataan yang Anda tentukan, dan membuang objek secara otomatis. Namun, using pernyataan ini hanya berguna untuk objek yang digunakan dalam cakupan metode di mana mereka dibangun.

Contoh berikut menggunakan pernyataan using untuk membuat dan merilis objek System.IO.StreamReader.

using System.IO;

class UsingStatement
{
    static void Main()
    {
        var buffer = new char[50];
        using (StreamReader streamReader = new("file1.txt"))
        {
            int charsRead = 0;
            while (streamReader.Peek() != -1)
            {
                charsRead = streamReader.Read(buffer, 0, buffer.Length);
                //
                // Process characters read.
                //
            }
        }
    }
}
Imports System.IO

Module UsingStatement
    Public Sub Main()
        Dim buffer(49) As Char
        Using streamReader As New StreamReader("File1.txt")
            Dim charsRead As Integer
            Do While streamReader.Peek() <> -1
                charsRead = streamReader.Read(buffer, 0, buffer.Length)
                ' 
                ' Process characters read.
                '
            Loop
        End Using
    End Sub
End Module

using Deklarasi adalah sintaks alternatif yang tersedia di mana kurung kurawal dihapus, dan cakupannya implisit.

using System.IO;

class UsingDeclaration
{
    static void Main()
    {
        var buffer = new char[50];
        using StreamReader streamReader = new("file1.txt");

        int charsRead = 0;
        while (streamReader.Peek() != -1)
        {
            charsRead = streamReader.Read(buffer, 0, buffer.Length);
            //
            // Process characters read.
            //
        }
    }
}

StreamReader Meskipun kelas mengimplementasikan IDisposable antarmuka, yang menunjukkan bahwa ia menggunakan sumber daya yang tidak dikelola, contohnya tidak secara eksplisit memanggil StreamReader.Dispose metode . Ketika kompilator C# atau Visual Basic menemukan pernyataan using, itu menghasilkan intermediate language (IL) yang setara dengan kode berikut yang secara eksplisit berisi blok try/finally.

using System.IO;

class TryFinallyGenerated
{
    static void Main()
    {
        var buffer = new char[50];
        StreamReader? streamReader = null;
        try
        {
            streamReader = new StreamReader("file1.txt");
            int charsRead = 0;
            while (streamReader.Peek() != -1)
            {
                charsRead = streamReader.Read(buffer, 0, buffer.Length);
                //
                // Process characters read.
                //
            }
        }
        finally
        {
            // If non-null, call the object's Dispose method.
            streamReader?.Dispose();
        }
    }
}
Imports System.IO

Module TryFinallyGenerated
    Public Sub Main()
        Dim buffer(49) As Char
        Dim streamReader As New StreamReader("File1.txt")
        Try
            Dim charsRead As Integer
            Do While streamReader.Peek() <> -1
                charsRead = streamReader.Read(buffer, 0, buffer.Length)
                ' 
                ' Process characters read.
                '
            Loop
        Finally
            If streamReader IsNot Nothing Then DirectCast(streamReader, IDisposable).Dispose()
        End Try
    End Sub
End Module

Pernyataan C# using juga memungkinkan Anda memperoleh beberapa sumber daya dalam satu pernyataan, yang secara internal setara dengan pernyataan berlapis using . Contoh berikut menginisialisasi dua objek StreamReader untuk membaca isi dari dua file berbeda.

using System.IO;

class SingleStatementMultiple
{
    static void Main()
    {
        var buffer1 = new char[50];
        var buffer2 = new char[50];

        using StreamReader version1 = new("file1.txt"),
                           version2 = new("file2.txt");

        int charsRead1, charsRead2 = 0;
        while (version1.Peek() != -1 && version2.Peek() != -1)
        {
            charsRead1 = version1.Read(buffer1, 0, buffer1.Length);
            charsRead2 = version2.Read(buffer2, 0, buffer2.Length);
            //
            // Process characters read.
            //
        }
    }
}

Coba/terakhir blokir

Alih-alih membungkus sebuah blok try/finally dalam pernyataan using, Anda dapat memilih untuk secara langsung mengimplementasikan try/finally blok. Ini mungkin gaya pengkodian pribadi Anda, atau Anda mungkin ingin melakukan ini karena salah satu alasan berikut:

  • Untuk menyertakan blok catch untuk menangani pengecualian yang dilemparkan pada blok try. Jika tidak, pengecualian apa pun yang dilemparkan dalam pernyataan using tidak tertangani.
  • Untuk menginstansiasi objek yang mengimplementasikan IDisposable dengan cakupan yang tidak lokal pada blok tempat ia dideklarasikan.

Contoh berikut mirip dengan contoh sebelumnya, kecuali bahwa contoh ini menggunakan blok try/catch/finally untuk membuat instans, menggunakan, dan membuang objek StreamReader, serta untuk menangani pengecualian apa pun yang dilemparkan oleh konstruktor StreamReader dan metode ReadToEnd. Kode dalam blok finally memeriksa bahwa objek yang mengimplementasikan IDisposable tidak null sebelum memanggil metode Dispose. Kegagalan untuk melakukan ini dapat mengakibatkan NullReferenceException pengecualian pada runtime.

using System;
using System.Globalization;
using System.IO;

class TryExplicitCatchFinally
{
    static void Main()
    {
        StreamReader? streamReader = null;
        try
        {
            streamReader = new StreamReader("file1.txt");
            string contents = streamReader.ReadToEnd();
            var info = new StringInfo(contents);
            Console.WriteLine($"The file has {info.LengthInTextElements} text elements.");
        }
        catch (FileNotFoundException)
        {
            Console.WriteLine("The file cannot be found.");
        }
        catch (IOException)
        {
            Console.WriteLine("An I/O error has occurred.");
        }
        catch (OutOfMemoryException)
        {
            Console.WriteLine("There is insufficient memory to read the file.");
        }
        finally
        {
            streamReader?.Dispose();
        }
    }
}
Imports System.Globalization
Imports System.IO

Module TryExplicitCatchFinally
    Sub Main()
        Dim streamReader As StreamReader = Nothing
        Try
            streamReader = New StreamReader("file1.txt")
            Dim contents As String = streamReader.ReadToEnd()
            Dim info As StringInfo = New StringInfo(contents)
            Console.WriteLine($"The file has {info.LengthInTextElements} text elements.")
        Catch e As FileNotFoundException
            Console.WriteLine("The file cannot be found.")
        Catch e As IOException
            Console.WriteLine("An I/O error has occurred.")
        Catch e As OutOfMemoryException
            Console.WriteLine("There is insufficient memory to read the file.")
        Finally
            If streamReader IsNot Nothing Then streamReader.Dispose()
        End Try
    End Sub
End Module

Anda dapat mengikuti pola dasar ini jika Anda memilih untuk menerapkan atau harus menerapkan blok try/finally, karena bahasa pemrograman Anda tidak mendukung pernyataan using, namun mengizinkan panggilan langsung ke metode Dispose.

Anggota objek IDisposable

Jika sebuah kelas memiliki bidang atau properti instans dan jenisnya mengimplementasikan IDisposable, maka kelas tersebut juga harus mengimplementasikan IDisposable. Untuk informasi selengkapnya, lihat Penerapan pembuangan berjenjang.

Lihat juga