Использование объектов, реализующих IDisposable
Сборщик мусора среды cl language (GC) освобождает память, используемую управляемыми объектами. Как правило, типы, использующие неуправляемые ресурсы, реализуют IDisposable или IAsyncDisposable интерфейс, чтобы разрешить восстановление неуправляемых ресурсов. Завершив использование объекта, реализующего IDisposableобъект, вызовите Dispose объект или DisposeAsync реализацию, чтобы явно выполнить очистку. Это можно сделать одним из двух способов.
- С помощью инструкции или объявления C#
using
(Using
в Visual Basic). - Путем реализации
try/finally
блока и вызова DisposeDisposeAsync метода в объекте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
.
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
Оператор using
в C# позволяет присоединять несколько ресурсов в одном операторе, что эквивалентно использованию вложенных инструкций 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. Перед вызовом метода Dispose код в блоке finally
проверяет, что объект, реализующий IDisposable, не является null
. Если этого сделать не удастся, это может привести к исключению 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. Дополнительные сведения см. в разделе "Реализация каскадного удаления".