Använda objekt som implementerar IDisposable
Common Language Runtimes skräpinsamlare (GC) återtar det minne som används av hanterade objekt. Vanligtvis implementerar IDisposable typer som använder ohanterade resurser eller IAsyncDisposable -gränssnittet så att ohanterade resurser kan frigöras. När du är klar med ett objekt som implementerar anropar IDisposabledu objektets Dispose eller DisposeAsync implementeringen för att uttryckligen utföra rensningen. Det kan du göra på ett av följande två vis:
- Med C#-
using
instruktionen eller deklarationen (Using
i Visual Basic). - Genom att implementera ett
try/finally
block och anropa Dispose metoden eller DisposeAsync ifinally
.
Viktigt!
GC tar inte bort dina objekt eftersom den inte har någon kunskap om IDisposable.Dispose() eller IAsyncDisposable.DisposeAsync(). GC vet bara om ett objekt kan slutföras (dvs. definierar en Object.Finalize() metod) och när objektets finaliserare måste anropas. Mer information finns i Så här fungerar slutförande. Mer information om hur du implementerar Dispose
och DisposeAsync
finns i:
Objekt som implementerar System.IDisposable eller System.IAsyncDisposable alltid ska tas bort korrekt, oavsett variabelomfång, om inget annat uttryckligen anges. Typer som definierar en finalizer för att frigöra ohanterade resurser anropar GC.SuppressFinalize vanligtvis från antingen deras Dispose
eller DisposeAsync
implementeringen. Anrop SuppressFinalize anger för GC att slutföraren redan har körts och att objektet inte bör höjas upp för slutförande.
Instruktionen using
Instruktionen using
i C# och -instruktionen Using
i Visual Basic förenklar koden som du måste skriva för att rensa ett objekt. -instruktionen using
hämtar en eller flera resurser, kör de instruktioner som du anger och tar bort objektet automatiskt. Instruktionen using
är dock endast användbar för objekt som används inom omfånget för den metod där de konstrueras.
I följande exempel används -instruktionen using
för att skapa och släppa ett System.IO.StreamReader objekt.
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
En using
deklaration är en alternativ syntax som är tillgänglig där klammerparenteserna tas bort och omfånget är implicit.
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 Även om klassen implementerar IDisposable gränssnittet, vilket indikerar att den använder en ohanterad resurs, anropar exemplet inte uttryckligen StreamReader.Dispose metoden. När C# eller Visual Basic-kompilatorn stöter på -instruktionen using
genererar den mellanliggande språk (IL) som motsvarar följande kod som uttryckligen innehåller ett try/finally
block.
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
Med C#- using
instruktionen kan du också hämta flera resurser i en enda instruktion, vilket är internt likvärdigt med kapslade using
instruktioner. I följande exempel instansieras två StreamReader objekt för att läsa innehållet i två olika filer.
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.
//
}
}
}
Försök/slutligen blockera
I stället för att omsluta ett try/finally
block i en using
-instruktion kan du välja att implementera try/finally
blocket direkt. Det kan vara din personliga kodningsstil, eller så kanske du vill göra detta av någon av följande orsaker:
- Inkludera ett
catch
block för att hantera undantag som genereras itry
blocket. Annars hanteras alla undantag som genereras i -instruktionenusing
. - Om du vill instansiera ett objekt som implementerar IDisposable vars omfång inte är lokalt för det block där det deklareras.
Följande exempel liknar föregående exempel, förutom att det använder ett try/catch/finally
block för att instansiera, använda och ta bort ett StreamReader objekt och hantera eventuella undantag som genereras av StreamReader konstruktorn och dess ReadToEnd metod. Koden i finally
blocket kontrollerar att objektet som implementerar IDisposable inte null
är innan det anropar Dispose metoden. Om du inte gör det kan det leda till ett NullReferenceException undantag vid körning.
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
Du kan följa det här grundläggande mönstret om du väljer att implementera eller måste implementera ett try/finally
block, eftersom programmeringsspråket inte stöder en using
instruktion, men tillåter direkta anrop till Dispose metoden.
IDisposable-instansmedlemmar
Om en klass äger ett instansfält eller en instansegenskap och dess typ implementerar IDisposablebör klassen även implementera IDisposable. Mer information finns i Implementera en kaskadborttagning.