公共语言运行时的垃圾回收器(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 语句
using
C# 中的语句和 Using
Visual Basic 中的语句简化了必须写入以清理对象的代码。 该 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
块,而不是用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。 有关详细信息,请参阅实现级联 Dispose。