Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
La méthode Dispose est principalement implémentée pour libérer des ressources non managées. Lorsque vous travaillez avec des membres d’instance qui sont des implémentations IDisposable, il est courant d’effectuer des appels Dispose en cascade. Il existe d’autres raisons d’implémenter Dispose, par exemple, pour libérer de la mémoire allouée, supprimer un élément ajouté à une collection ou signaler la libération d’un verrou acquis.
Le récupérateur de mémoire .NET n’alloue pas de mémoire non managée, et n’en libère pas non plus. Le modèle de suppression d’un objet, appelé modèle de suppression, impose l’ordre sur la durée de vie d’un objet. Le pattern dispose est utilisé pour les objets qui implémentent l’interface IDisposable. Ce modèle est courant lors de l’interaction avec des handles de fichiers et de canaux, des handles de Registre, des handles d’attente ou des pointeurs vers des blocs de mémoire non managée, car le récupérateur de mémoire ne peut pas récupérer des objets non managés.
Afin de garantir que les ressources sont toujours nettoyées de manière appropriée, une méthode Dispose doit être idempotente, afin qu'elle puisse être appelée plusieurs fois sans lever d'exception. En outre, les appels ultérieurs de Dispose ne doivent rien faire.
L’exemple de code fourni pour la méthode GC.KeepAlive montre comment le nettoyage de la mémoire peut entraîner l’exécution d’un finaliseur pendant qu’une référence non managée à l’objet ou à ses membres est toujours en cours d’utilisation. Il peut être judicieux d’utiliser GC.KeepAlive pour rendre l’objet inéligible pour la collecte de déchets du début de la routine actuelle jusqu’au point où cette méthode est appelée.
Conseil
En ce qui concerne l’injection de dépendances, lors de l’inscription de services dans un IServiceCollection, la durée de vie du service est gérée implicitement en votre nom. Les IServiceProvider et les IHost correspondants coordonnent le nettoyage des ressources. Plus précisément, les implémentations de IDisposable et de IAsyncDisposable sont correctement supprimées à la fin de leur durée de vie spécifiée.
Pour plus d’informations, consultez Injection de dépendances dans .NET.
Appels de suppression en cascade
Si votre classe possède une instance d’un autre type qui implémente IDisposable, la classe contenante elle-même doit également implémenter IDisposable. En règle générale, une classe qui instancie une IDisposable implémentation et la stocke en tant que membre d’instance (ou propriété) est également responsable de son nettoyage. Cela permet de s'assurer que les types jetables référencés ont la possibilité d'effectuer un nettoyage de manière déterministe par le biais de la méthode Dispose. Dans l’exemple suivant, la classe est sealed (ou NotInheritable en Visual Basic).
using System;
public sealed class Foo : IDisposable
{
private readonly IDisposable _bar;
public Foo()
{
_bar = new Bar();
}
public void Dispose() => _bar.Dispose();
}
Public NotInheritable Class Foo
Implements IDisposable
Private ReadOnly _bar As IDisposable
Public Sub New()
_bar = New Bar()
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
_bar.Dispose()
End Sub
End Class
Conseil
- Si votre classe a un champ ou une IDisposable propriété, mais ne la possède pas, la classe n’a pas besoin d’implémenter IDisposable. En règle générale, une classe créant et stockant l’objet IDisposable enfant devient également le propriétaire, mais dans certains cas, la propriété peut être transférée vers un autre IDisposable type.
- Il existe des cas où vous souhaiterez peut-être effectuer
nullla vérification dans un finaliseur (qui inclut laDispose(false)méthode appelée par un finaliseur). L’une des principales raisons est si vous ne savez pas si l’instance a été entièrement initialisée (par exemple, une exception peut être levée dans un constructeur).
Dispose() et Dispose(bool)
L’interface IDisposable nécessite l’implémentation d’une méthode sans paramètre unique, Dispose. En outre, toute classe non scellée doit avoir une méthode de surcharge Dispose(bool).
Les signatures de méthode sont les suivantes :
-
publicnon virtuel (NotOverridableen Visual Basic) ( implémentationIDisposable.Dispose). -
protected virtual(Overridableen Visual Basic)Dispose(bool).
Méthode Dispose()
Étant donné que la méthode public, non virtuelle (NotOverridable en Visual Basic), la méthode Dispose sans paramètre est appelée lorsqu’elle n’est plus nécessaire (par un consommateur du type), son objectif est de libérer des ressources non managées, d’effectuer un nettoyage général et d’indiquer que le finaliseur, le cas échéant, n’a pas à s’exécuter. La libération de la mémoire réelle associée à un objet managé est toujours du domaine du récupérateur de mémoire. En raison de cela, il a une implémentation standard :
public void Dispose()
{
// Dispose of unmanaged resources.
Dispose(true);
// Suppress finalization.
GC.SuppressFinalize(this);
}
Public Sub Dispose() _
Implements IDisposable.Dispose
' Dispose of unmanaged resources.
Dispose(True)
' Suppress finalization.
GC.SuppressFinalize(Me)
End Sub
La méthode Dispose effectue le nettoyage de tous les objets, le récupérateur de mémoire n'a plus donc besoin d'appeler la remplacement de Object.Finalize des objets. Par conséquent, l'appel à la méthode SuppressFinalize empêche le récupérateur de mémoire d'exécuter le finaliseur. Si le type n’a pas de finaliseur, l’appel à GC.SuppressFinalize n’a aucun effet. Le nettoyage réel est effectué par la surcharge de méthode Dispose(bool).
Surcharge de méthode Dispose(bool)
Dans la surcharge, le paramètre disposing est un Boolean qui indique si l’appel de méthode provient d’une méthode Dispose (sa valeur est true) ou d’un finaliseur (sa valeur est false).
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
// Dispose managed state (managed objects).
// ...
}
// Free unmanaged resources.
// ...
_disposed = true;
}
Protected Overridable Sub Dispose(disposing As Boolean)
If disposed Then Exit Sub
If disposing Then
' Free managed resources.
' ...
End If
' Free unmanaged resources.
' ...
disposed = True
End Sub
Important
Le paramètre disposing doit être false lorsqu’il est appelé à partir d’un finaliseur et true lorsqu’il est appelé à partir de la méthode IDisposable.Dispose. En d’autres termes, il est true lorsqu’il est appelé de façon déterministe et false lorsqu’il n’est pas déterministement appelé.
Le corps de la méthode se compose de trois blocs de code :
Bloc de retour conditionnel si l’objet est déjà supprimé.
Bloc conditionnel qui libère des ressources managées. Ce bloc s’exécute si la valeur de
disposingesttrue. Les ressources managées qu’il libère peuvent inclure :- Objets managés qui implémentent IDisposable. Le bloc conditionnel peut être utilisé pour appeler leur implémentation de Dispose (suppression en cascade). Si vous avez utilisé une classe dérivée de System.Runtime.InteropServices.SafeHandle pour encapsuler votre ressource non managée, vous devez appeler l’implémentation SafeHandle.Dispose() ici.
- Objets managés qui consomment de grandes quantités de mémoire ou consomment des ressources rares. Assignez les références des objets gérés volumineux à
nullpour les rendre moins accessibles. Cela les libère plus rapidement qu’en cas de récupération non déterministe.
Bloc qui libère des ressources non managées. Ce bloc s’exécute indépendamment de la valeur du paramètre
disposing.
Si l’appel de méthode provient d’un finaliseur, seul le code qui libère les ressources non managées doit s’exécuter. L’implémenteur est chargé de s’assurer que le chemin d’accès faux n’interagit pas avec les objets managés qui ont peut-être été supprimés. Cela est important, car l’ordre dans lequel le garbage collector supprime les objets managés pendant la finalisation n’est pas déterministe.
Implémenter le modèle Dispose
Toutes les classes non scellées (ou les classes Visual Basic non modifiées comme NotInheritable) doivent être considérées comme une classe de base potentielle, car elles peuvent être héritées. Si vous implémentez le modèle de suppression pour une classe de base potentielle, vous devez ajouter les méthodes suivantes à votre classe :
- Implémentation Dispose qui appelle la méthode
Dispose(bool). - Méthode
Dispose(bool)qui effectue le nettoyage réel. - Si votre classe traite des ressources non managées, fournissez un remplacement à la Object.Finalize méthode ou encapsulez la ressource non managée dans un SafeHandle.
Important
Un finaliseur ( Object.Finalize remplacement) n’est requis que si vous référencez directement des ressources non managées. Il s’agit d’un scénario très avancé qui peut généralement être évité :
- Si votre classe référence uniquement des objets managés, il est toujours possible que la classe implémente le modèle de suppression. Il n’est pas nécessaire d’implémenter un finaliseur.
- Si vous avez besoin de gérer des ressources non managées, nous vous recommandons vivement d’encapsuler IntPtr le handle non managé dans un SafeHandle. Le SafeHandle finaliseur est fourni pour que vous n'ayez pas à en écrire un vous-même. Pour plus d’informations, consultez le paragraphe Poignées sûres.
Classe de base avec des ressources managées
Voici un exemple général d’implémentation du modèle de suppression pour une classe de base qui possède uniquement des ressources managées.
using System;
using System.IO;
public class DisposableBase : IDisposable
{
// Detect redundant Dispose() calls.
private bool _isDisposed;
// Instantiate a disposable object owned by this class.
private Stream? _managedResource = new MemoryStream();
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
_isDisposed = true;
if (disposing)
{
// Dispose managed state.
_managedResource?.Dispose();
_managedResource = null;
}
}
}
}
Imports System.IO
Public Class DisposableBase
Implements IDisposable
' Detect redundant Dispose() calls.
Private _isDisposed As Boolean
' Instantiate a disposable object owned by this class.
Private _managedResource As Stream = New MemoryStream()
' Public implementation of Dispose pattern callable by consumers.
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
' Protected implementation of Dispose pattern.
Protected Overridable Sub Dispose(disposing As Boolean)
If Not _isDisposed Then
_isDisposed = True
If disposing Then
' Dispose managed state.
_managedResource?.Dispose()
_managedResource = Nothing
End If
End If
End Sub
End Class
Remarque
L’exemple précédent utilise un objet facticeMemoryStream pour illustrer le modèle. N'importe quel IDisposable pourrait être utilisé à la place.
Classe de base avec des ressources non managées
Voici un exemple d'implémentation du modèle d'élimination d'une classe de base qui remplace Object.Finalize pour nettoyer les ressources non managées qu'elle possède. L’exemple montre également un moyen d’implémenter Dispose(bool) de manière thread-safe. La synchronisation peut être critique lors du traitement des ressources non managées dans une application multithread. Comme mentionné précédemment, il s’agit d’un scénario avancé.
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
public class DisposableBaseWithFinalizer : IDisposable
{
// Detect redundant Dispose() calls in a thread-safe manner.
// _isDisposed == 0 means Dispose(bool) has not been called yet.
// _isDisposed == 1 means Dispose(bool) has been already called.
private int _isDisposed;
// Instantiate a disposable object owned by this class.
private Stream? _managedResource = new MemoryStream();
// A pointer to 10 bytes allocated on the unmanaged heap.
private IntPtr _unmanagedResource = Marshal.AllocHGlobal(10);
~DisposableBaseWithFinalizer() => Dispose(false);
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
// In case _isDisposed is 0, atomically set it to 1.
// Enter the branch only if the original value is 0.
if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
{
if (disposing)
{
_managedResource?.Dispose();
_managedResource = null;
}
Marshal.FreeHGlobal(_unmanagedResource);
}
}
}
Imports System
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Threading
Public Class DisposableBaseWithFinalizer
Implements IDisposable
' Detect redundant Dispose() calls in a thread-safe manner.
' _isDisposed == 0 means Dispose(bool) has not been called yet.
' _isDisposed == 1 means Dispose(bool) has been already called.
Private _isDisposed As Integer
' Instantiate a disposable object owned by this class.
Private _managedResource As Stream = New MemoryStream()
' A pointer to 10 bytes allocated on the unmanaged heap.
Private _unmanagedResource As IntPtr = Marshal.AllocHGlobal(10)
Protected Overrides Sub Finalize()
Dispose(False)
End Sub
' Public implementation of Dispose pattern callable by consumers.
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
' Protected implementation of Dispose pattern.
Protected Overridable Sub Dispose(disposing As Boolean)
' In case _isDisposed is 0, atomically set it to 1.
' Enter the branch only if the original value is 0.
If Interlocked.CompareExchange(_isDisposed, 1, 0) = 0 Then
If disposing Then
_managedResource?.Dispose()
_managedResource = Nothing
End If
Marshal.FreeHGlobal(_unmanagedResource)
End If
End Sub
End Class
Remarque
- L’exemple précédent utilise AllocHGlobal pour allouer 10 octets sur le tas non managé dans le constructeur et libérer la mémoire tampon en
Dispose(bool)appelant FreeHGlobal. Il s’agit d’une allocation factice à des fins d’illustration. - Là encore, il est recommandé d’éviter d’implémenter un finaliseur. Consultez Implémenter le modèle de suppression à l’aide d’un handle sécurisé personnalisé pour un équivalent de l’exemple précédent qui délègue la finalisation et la synchronisation non déterministes à SafeHandle.
Conseil
En C#, vous implémentez une finalisation en fournissant un finaliseur, et non en substituant Object.Finalize. Dans Visual Basic, vous créez un finaliseur avec Protected Overrides Sub Finalize().
Implémenter le modèle de suppression pour une classe dérivée
Une classe dérivée d’une classe qui implémente l’interface IDisposable ne doit pas implémenter IDisposable, car l’implémentation de classe de base de IDisposable.Dispose est héritée par ses classes dérivées. Au lieu de cela, pour nettoyer une classe dérivée, vous fournissez les éléments suivants :
- Méthode
protected override void Dispose(bool)qui remplace la méthode de classe de base et effectue le nettoyage réel de la classe dérivée. Cette méthode doit également appeler la méthodebase.Dispose(bool)(MyBase.Dispose(bool)en Visual Basic) en lui transmettant l’état de suppression (paramètrebool disposing) en tant qu’argument. - Une classe dérivée de SafeHandle qui encapsule votre ressource non managée (recommandée) ou une substitution à la méthode Object.Finalize. La classe SafeHandle fournit un finaliseur qui vous libère d’avoir à coder un. Si vous fournissez un finaliseur, il doit appeler la surcharge
Dispose(bool)avec un argumentfalse.
Voici un exemple du modèle général d’implémentation du modèle Dispose pour une classe dérivée qui utilise un handle sécurisé :
using System.IO;
public class DisposableDerived : DisposableBase
{
// To detect redundant calls
private bool _isDisposed;
// Instantiate a disposable object owned by this class.
private Stream? _managedResource = new MemoryStream();
// Protected implementation of Dispose pattern.
protected override void Dispose(bool disposing)
{
if (!_isDisposed)
{
_isDisposed = true;
if (disposing)
{
_managedResource?.Dispose();
_managedResource = null;
}
}
// Call base class implementation.
base.Dispose(disposing);
}
}
Imports System.IO
Public Class DisposableDerived
Inherits DisposableBase
' To detect redundant calls
Private _isDisposed As Boolean
' Instantiate a disposable object owned by this class.
Private _managedResource As Stream = New MemoryStream()
' Protected implementation of Dispose pattern.
Protected Overrides Sub Dispose(disposing As Boolean)
If Not _isDisposed Then
_isDisposed = True
If disposing Then
_managedResource?.Dispose()
_managedResource = Nothing
End If
End If
' Call base class implementation.
MyBase.Dispose(disposing)
End Sub
End Class
Remarque
L’exemple précédent utilise un objet SafeFileHandle pour illustrer le modèle ; tout objet dérivé de SafeHandle peut être utilisé à la place. Notez que l’exemple n’instancie pas correctement son SafeFileHandle objet.
Voici le modèle général d’implémentation du modèle de suppression pour une classe dérivée qui remplace Object.Finalize:
using System.Threading;
public class DisposableDerivedWithFinalizer : DisposableBaseWithFinalizer
{
// Detect redundant Dispose() calls in a thread-safe manner.
// _isDisposed == 0 means Dispose(bool) has not been called yet.
// _isDisposed == 1 means Dispose(bool) has been already called.
private int _isDisposed;
~DisposableDerivedWithFinalizer() => Dispose(false);
// Protected implementation of Dispose pattern.
protected override void Dispose(bool disposing)
{
// In case _isDisposed is 0, atomically set it to 1.
// Enter the branch only if the original value is 0.
if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
{
if (disposing)
{
// TODO: dispose managed state (managed objects).
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
}
// Call the base class implementation.
base.Dispose(disposing);
}
}
Imports System.Threading
Public Class DisposableDerivedWithFinalizer
Inherits DisposableBaseWithFinalizer
' Detect redundant Dispose() calls in a thread-safe manner.
' _isDisposed == 0 means Dispose(bool) has not been called yet.
' _isDisposed == 1 means Dispose(bool) has been already called.
Private _isDisposed As Integer
Protected Overrides Sub Finalize()
Dispose(False)
End Sub
' Protected implementation of Dispose pattern.
Protected Overrides Sub Dispose(disposing As Boolean)
' In case _isDisposed is 0, atomically set it to 1.
' Enter the branch only if the original value is 0.
If Interlocked.CompareExchange(_isDisposed, 1, 0) = 0 Then
If disposing Then
' TODO: dispose managed state (managed objects).
End If
' TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
' TODO: set large fields to null.
End If
' Call the base class implementation.
MyBase.Dispose(disposing)
End Sub
End Class
Poignées sûres
L’écriture de code pour le finaliseur d’un objet est une tâche complexe qui peut entraîner des problèmes s’il n’est pas effectué correctement. Par conséquent, nous vous recommandons de construire System.Runtime.InteropServices.SafeHandle objets au lieu d’implémenter un finaliseur.
Un System.Runtime.InteropServices.SafeHandle est un type managé abstrait qui encapsule un System.IntPtr qui identifie une ressource non managée. Sur Windows, il peut identifier un handle et sur Unix, un descripteur de fichier. Le SafeHandle fournit toute la logique nécessaire pour s’assurer que cette ressource est libérée une seule fois, soit lorsque l'SafeHandle est supprimé, soit lorsque toutes les références à l'SafeHandle ont été supprimées et que l’instance de SafeHandle est finalisée.
La System.Runtime.InteropServices.SafeHandle est une classe de base abstraite. Les classes dérivées fournissent des instances spécifiques pour différents types de poignées. Ces classes dérivées vérifient quelles valeurs de l'System.IntPtr sont considérées comme invalides et comment libérer réellement la poignée. Par exemple, SafeFileHandle dérive de SafeHandle pour encapsuler IntPtrs qui identifient les handles de fichiers ouverts et les descripteurs, et redéfinit sa méthode SafeHandle.ReleaseHandle() pour les fermer (via la fonction close sur Unix ou la fonction CloseHandle sur Windows). La plupart des API de .NET dans les bibliothèques qui créent une ressource non managée l’encapsulent dans une SafeHandle et vous retournent ce SafeHandle selon les besoins, plutôt que de remettre le pointeur brut. Dans les situations où vous interagissez avec un composant non managé et obtenez une IntPtr pour une ressource non managée, vous pouvez créer votre propre type de SafeHandle pour l’encapsuler. Par conséquent, peu de types non-SafeHandle ont besoin d'implémenter des finaliseurs. La plupart des implémentations de modèles jetables se contentent d'encapsuler d'autres ressources gérées, y compris parfois des objets SafeHandle.
Implémenter le modèle de suppression à l’aide d’un handle sécurisé personnalisé
Le code suivant montre comment gérer des ressources non managées en implémentant un SafeHandle.
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
// Wraps the IntPtr allocated by Marshal.AllocHGlobal() into a SafeHandle.
class LocalAllocHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private LocalAllocHandle() : base(ownsHandle: true) { }
// No need to implement a finalizer - SafeHandle's finalizer will call ReleaseHandle for you.
protected override bool ReleaseHandle()
{
Marshal.FreeHGlobal(handle);
return true;
}
// Allocate bytes with Marshal.AllocHGlobal() and wrap the result into a SafeHandle.
public static LocalAllocHandle Allocate(int numberOfBytes)
{
IntPtr nativeHandle = Marshal.AllocHGlobal(numberOfBytes);
LocalAllocHandle safeHandle = new LocalAllocHandle();
safeHandle.SetHandle(nativeHandle);
return safeHandle;
}
}
public class DisposableBaseWithSafeHandle : IDisposable
{
// Detect redundant Dispose() calls.
private bool _isDisposed;
// Managed disposable objects owned by this class
private LocalAllocHandle? _safeHandle = LocalAllocHandle.Allocate(10);
private Stream? _otherUnmanagedResource = new MemoryStream();
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
_isDisposed = true;
if (disposing)
{
// Dispose managed state.
_otherUnmanagedResource?.Dispose();
_safeHandle?.Dispose();
_otherUnmanagedResource = null;
_safeHandle = null;
}
}
}
}
Imports System
Imports System.IO
Imports System.Runtime.InteropServices
Imports Microsoft.Win32.SafeHandles
' Wraps the IntPtr allocated by Marshal.AllocHGlobal() into a SafeHandle.
Public Class LocalAllocHandle
Inherits SafeHandleZeroOrMinusOneIsInvalid
Private Sub New()
MyBase.New(True)
End Sub
' No need to implement a finalizer - SafeHandle's finalizer will call ReleaseHandle for you.
Protected Overrides Function ReleaseHandle() As Boolean
Marshal.FreeHGlobal(handle)
Return True
End Function
' Allocate bytes with Marshal.AllocHGlobal() and wrap the result into a SafeHandle.
Public Shared Function Allocate(numberOfBytes As Integer) As LocalAllocHandle
Dim nativeHandle As IntPtr = Marshal.AllocHGlobal(numberOfBytes)
Dim safeHandle As New LocalAllocHandle()
safeHandle.SetHandle(nativeHandle)
Return safeHandle
End Function
End Class
Public Class DisposableBaseWithSafeHandle
Implements IDisposable
' Detect redundant Dispose() calls.
Private _isDisposed As Boolean
' Managed disposable objects owned by this class
Private _safeHandle As LocalAllocHandle = LocalAllocHandle.Allocate(10)
Private _otherUnmanagedResource As Stream = New MemoryStream()
' Public implementation of Dispose pattern callable by consumers.
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
' Protected implementation of Dispose pattern.
Protected Overridable Sub Dispose(disposing As Boolean)
If Not _isDisposed Then
_isDisposed = True
If disposing Then
' Dispose managed state.
_otherUnmanagedResource?.Dispose()
_safeHandle?.Dispose()
_otherUnmanagedResource = Nothing
_safeHandle = Nothing
End If
End If
End Sub
End Class
Remarque
Le comportement de la DisposableBaseWithSafeHandle classe équivaut au comportement de la DisposableBaseWithFinalizer classe dans un exemple précédent, mais l’approche illustrée ici est plus sûre :
- Il n’est pas nécessaire d’implémenter un finaliseur, car SafeHandle il s’occupera de la finalisation.
- Il n’est pas nécessaire de synchroniser pour garantir la sécurité des threads. Même s’il existe une condition de course dans l’implémentation
DisposedeDisposableBaseWithSafeHandle, SafeHandle garantit que SafeHandle.ReleaseHandle n’est appelé qu’une seule fois.
Handles sécurisés intégrés dans .NET
Les classes dérivées suivantes dans l’espace de noms Microsoft.Win32.SafeHandles fournissent des handles sécurisés.
| Classe | Ressources qu’il contient |
|---|---|
| SafeFileHandle SafeMemoryMappedFileHandle SafePipeHandle |
Fichiers, fichiers mappés en mémoire et canaux |
| SafeMemoryMappedViewHandle | Vues de la mémoire |
| SafeNCryptKeyHandle SafeNCryptProviderHandle SafeNCryptSecretHandle |
Constructions de chiffrement |
| SafeRegistryHandle | Clés de Registre |
| SafeWaitHandle | Handles d'attente |