使用實作 IDisposable 的物件

Common Language Runtime 的記憶體回收行程 (GC) 會回收受控物件所使用的記憶體。 一般而言,使用非受控資源的類型會實作 IDisposableIAsyncDisposable 介面,以允許回收非受控資源。 當您完成使用實作 IDisposable 的物件時,您會呼叫物件的 DisposeDisposeAsync 實作來明確執行清除。 您可以使用下列其中一種作法:

  • 使用 C# using 陳述式或宣告 (在 Visual Basic 中為 Using)。
  • 藉由實作 try/finally 區塊,並在 finally 中呼叫 DisposeDisposeAsync 方法。

重要

GC 不會處置您的物件,因為它並不瞭解 IDisposable.Dispose()IAsyncDisposable.DisposeAsync()。 GC 只知道物件是否為可完成的 (也就是說,它會定義 Object.Finalize() 方法),以及何時需要呼叫物件的完成項。 如需詳細資訊,請參閱最終處理的運作方式。 如需實作 DisposeDisposeAsync 的其他詳細資料,請參閱:

除非另有明確說明,否則不論變數範圍設定為何,都應該一律正確處置實作 System.IDisposableSystem.IAsyncDisposable 的物件。 定義完成項以釋放非受控資源的型別通常會從其 DisposeDisposeAsync 實作呼叫 GC.SuppressFinalize。 呼叫 SuppressFinalize 會向 GC 指出已經執行完成項,而且不應該升級物件以進行最終處理。

using 陳述式

C# 中的 using 陳述式和 Visual Basic 中的 Using 陳述式可簡化清除物件所必須撰寫的程式碼。 using 陳述式會取得一項或多項資源、執行您指定的陳述式,然後自動處置該物件。 不過,using 陳述式只對建構物件的方法範圍內所使用的物件有其實用之處。

下列範例會使用 using 陳述式建立和發行 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 宣告是已移除大括弧且範圍設定隱含可用的替代語法。

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 類別會實作 IDisposable 介面,指出它使用的是非受控資源,但是這個範例並不會明確呼叫 StreamReader.Dispose 方法。 當 C# 或 Visual Basic 編譯器遇到 using 陳述式時,會發出相當於下列明確包含 try/finally 區塊之程式碼的中繼語言 (IL)。

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

C# using 陳述式還可讓您以單一陳述式取得多項資源,其內部相當於巢狀的 using 陳述式。 下列範例會將兩個 StreamReader 物件具現化,以便讀取兩個不同檔案的內容。

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.
            //
        }
    }
}

Try/finally 區塊

您可以選擇直接實作 try/finally 區塊,而不將 try/finally 區塊包裝在 using 陳述式中。 它可成為您的個人編碼風格,也可能基於下列其中一個原因而這樣做:

  • 包含 catch 區塊以處理 try 區塊中擲回的例外狀況。 否則,不會處理 using 陳述式內擲回的任何例外狀況。
  • 若要將實作 IDisposable 且範圍對於物件宣告所在區塊並非區域的物件具現化。

下列範例類似上述範例,不過它會使用 try/catch/finally 區塊具現化、使用和處置 StreamReader 物件,以及處理 StreamReader 建構函式和其 ReadToEnd 方法擲回的所有例外狀況。 在 finally 區塊中的程式碼會先確認實作 IDisposable 的物件不是 null,再呼叫 Dispose 方法。 若未這樣做,可能導致執行階段產生 NullReferenceException 例外狀況。

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

如果您的程式語言不支援 using 陳述式,但是允許直接呼叫 Dispose 方法,而使得您選擇實作或必須實作 try/finally 區塊,則可以遵循這個基本模式。

IDisposable 執行個體成員

如果類別擁有執行個體欄位或屬性,且其型別實作 IDisposable,則該類別也應該實作 IDisposable。 如需詳細資訊,請參閱實作串聯處置

另請參閱