公用語言執行平台的垃圾收集器(GC)會回收受管理物件所使用的記憶體。 一般而言,使用非管理資源的類型會實作 IDisposable 或 IAsyncDisposable 介面,以允許回收這些非管理資源。 當您完成使用實作 IDisposable的物件時,會呼叫 物件的 Dispose 或 DisposeAsync 實作,以明確執行清除。 您可以使用下列兩種方式之一來執行此動作:
- 使用 C#
using
語句或宣告 (Using
在 Visual Basic 中)。 - 藉由實作
try/finally
區塊,並在 Dispose 中呼叫 DisposeAsync 或finally
方法。
這很重要
GC 不會 處置您的物件,因為它不知道 IDisposable.Dispose() 或 IAsyncDisposable.DisposeAsync()。 GC 只知道物件是否可完成(也就是它定義 Object.Finalize() 方法),以及何時需要呼叫物件的完成項。 如需詳細資訊,請參閱 最終處理的運作方式。 如需實作 Dispose
和 DisposeAsync
的其他詳細數據,請參閱:
實作System.IDisposable或System.IAsyncDisposable的物件,不論變數範圍如何,一律應該妥善處置,除非另有明確說明。 定義完成項以釋放非受控資源的型別,通常會從其GC.SuppressFinalize或Dispose
實作中呼叫DisposeAsync
。 呼叫 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 介面,指出其使用 Unmanaged 資源,但此範例不會明確呼叫 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
區塊,而不是將using
區塊包裝在try/finally
語句中。 這可能是您的個人程式代碼撰寫樣式,或基於下列其中一個原因而想要這麼做:
- 若要包含
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
如果您選擇實作 或必須實 try/finally
作 區塊,則可以遵循此基本模式,因為您的程式設計語言不支援 using
語句,但允許直接呼叫 Dispose 方法。
IDisposable 實例成員
如果類別擁有實體欄位或屬性,且其型別實作IDisposable,則該類別也應該實作IDisposable。 如需詳細資訊,請參閱 實作串聯處置。