使用實作 IDisposable 的物件
Common Language Runtime 的記憶體回收行程 (GC) 會回收受控物件所使用的記憶體。 一般而言,使用非受控資源的類型會實作 IDisposable 或 IAsyncDisposable 介面,以允許回收非受控資源。 當您完成使用實作 IDisposable 的物件時,您會呼叫物件的 Dispose 或 DisposeAsync 實作來明確執行清除。 您可以使用下列其中一種作法:
- 使用 C#
using
陳述式或宣告 (在 Visual Basic 中為Using
)。 - 藉由實作
try/finally
區塊,並在finally
中呼叫 Dispose 或 DisposeAsync 方法。
重要
GC 不會處置您的物件,因為它並不瞭解 IDisposable.Dispose() 或 IAsyncDisposable.DisposeAsync()。 GC 只知道物件是否為可完成的 (也就是說,它會定義 Object.Finalize() 方法),以及何時需要呼叫物件的完成項。 如需詳細資訊,請參閱最終處理的運作方式。 如需實作 Dispose
和 DisposeAsync
的其他詳細資料,請參閱:
除非另有明確說明,否則不論變數範圍設定為何,都應該一律正確處置實作 System.IDisposable 或 System.IAsyncDisposable 的物件。 定義完成項以釋放非受控資源的型別通常會從其 Dispose
或 DisposeAsync
實作呼叫 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。 如需詳細資訊,請參閱實作串聯處置。