Utilisation d’objets implémentant IDisposable
Le récupérateur de mémoire (GC) du Common Language Runtime récupère la mémoire utilisée par les objets managés. En règle générale, les types qui utilisent des ressources non managées implémentent l’interface IDisposable ou IAsyncDisposable pour permettre la récupération des ressources non managées. Lorsque vous avez terminé d’utiliser un objet qui implémente IDisposable, vous appelez l’implémentation de l’objet Dispose ou DisposeAsync pour effectuer explicitement le nettoyage. Vous pouvez le faire de deux façons :
- Avec l’instruction ou la déclaration
using
C# (Using
en Visual Basic). - En implémentant un bloc
try/finally
et en appelant la méthode Dispose ou DisposeAsync dans lefinally
.
Important
Le GC ne supprime pas vos objets, car il n’a aucune connaissance de IDisposable.Dispose() ou IAsyncDisposable.DisposeAsync(). Le GC sait uniquement si un objet est finalisable (autrement dit, il définit une méthode Object.Finalize()) et quand le finaliseur de l’objet doit être appelé. Pour plus d’informations, consultez Fonctionnement de la finalisation. Pour plus d’informations sur l’implémentation Dispose
et DisposeAsync
, consultez :
Les objets qui implémentent System.IDisposable ou System.IAsyncDisposable doivent toujours être supprimés correctement, quelle que soit l’étendue de la variable, sauf indication contraire explicite. Les types qui définissent un finaliseur pour libérer des ressources non managées appellent généralement GC.SuppressFinalize à partir de leur implémentation Dispose
ou DisposeAsync
. L’appel à SuppressFinalize indique au GC que le finaliseur a déjà été exécuté et que l’objet ne doit pas être promu pour la finalisation.
Instruction using
L’instruction using
en C# et l’instruction Using
en Visual Basic simplifient le code que vous devez écrire pour nettoyer un objet. L’instruction using
obtient une ou plusieurs ressources, exécute les instructions que vous spécifiez, puis supprime automatiquement l’objet. Toutefois, l’instruction using
est utile uniquement pour les objets utilisés dans la portée de la méthode dans laquelle elles sont construites.
L’exemple suivant utilise l’instruction using
pour créer et libérer un objet 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
Une déclaration using
est une autre syntaxe disponible où les accolades sont supprimées, et l’étendue est implicite.
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.
//
}
}
}
Bien que la classe StreamReader implémente l’interface IDisposable, ce qui signifie qu’elle utilise une ressource non managée, l’exemple n’appelle pas explicitement la méthode StreamReader.Dispose. Quand le compilateur C# ou Visual Basic rencontre l’instruction using
, il émet en langage intermédiaire qui est équivalent au code suivant contenant explicitement un bloc 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
L’instruction using
en C# vous permet d’acquérir plusieurs ressources dans une seule instruction, ce qui équivaut en interne à des instructions using
imbriquées. L'exemple suivant instancie deux objets StreamReader pour lire le contenu de deux fichiers différents.
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.
//
}
}
}
Bloc try/finally
Au lieu d’encapsuler un bloc try/finally
dans une instruction using
, vous pouvez choisir d’implémenter le bloc try/finally
directement. Il peut s’agir de votre style personnel en matière de programmation, ou vous pouvez procéder ainsi pour l’une des raisons suivantes :
- Pour insérer un bloc
catch
afin de gérer les exceptions levées dans le bloctry
. Autrement, toutes les exceptions levées dans l’instructionusing
ne sont pas gérées. - Pour instancier un objet qui implémente IDisposable dont la portée n'est pas locale au bloc dans lequel elle est déclarée.
L'exemple suivant est similaire à l'exemple précédent, mais il utilise un bloc try/catch/finally
pour instancier, utiliser et supprimer un objet StreamReader, ainsi que pour gérer les exceptions levées par le constructeur StreamReader et sa méthode ReadToEnd. Le code du bloc finally
vérifie que l’objet qui implémente IDisposable n’est pas null
avant d’appeler la méthode Dispose. La non-exécution de cette opération peut générer une NullReferenceException au moment de l'exécution.
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
Vous pouvez suivre ce modèle de base si vous choisissez d’implémenter (ou que vous devez implémenter) un bloc try/finally
, car votre langage de programmation ne prend pas en charge l’instruction using
, mais autorise des appels directs à la méthode Dispose.
Membres d’instance IDisposable
Si une classe possède un champ ou une propriété d’instance et que son type implémente IDisposable, la classe doit également implémenter IDisposable. Pour plus d’informations, consultez Implémenter une suppression en cascade.