Implémentation des méthodes Finalize et Dispose pour nettoyer des ressources non managées
Notes
Pour plus d'informations sur la finalisation et la suppression à l'aide de C++, consultez Destructors and Finalizers in Visual C++.
Les instances de classe encapsulent souvent un contrôle des ressources qui ne sont pas managées par le runtime, telles que les handles de fenêtre (HWND), les connexions de base de données et ainsi de suite. C'est pourquoi vous devez fournir un moyen implicite et explicite de libérer ces ressources. Fournissez un contrôle implicite en implémentant la méthode Finalize protégée sur un objet (syntaxe du destructeur en C# et C++). Le « garbage collector » appelle cette méthode quand il n'y a plus de référence valide à l'objet.
Dans certains cas, il est possible de fournir aux programmeurs utilisant un objet la possibilité de libérer explicitement ces ressources externes avant que le « garbage collector » ne libère l'objet. Si une ressource externe est rare ou coûteuse, les performances peuvent être améliorées si le programmeur libère explicitement des ressources lorsqu'elles ne sont plus utilisées. Pour fournir un contrôle explicite, implémentez la méthode Dispose fournie par l'interface IDisposable. Le consommateur de l'objet doit appeler cette méthode lorsqu'il a terminé d'utiliser l'objet. Il est possible d'appeler Dispose même si d'autres références à l'objet sont actives.
Notez que vous devez fournir un nettoyage implicite à l'aide de la méthode Finalize, même quand vous fournissez un contrôle explicite avec Dispose. Finalize fournit une sauvegarde permettant d'éviter une fuite définitive des ressources si le programmeur n'appelle pas Dispose.
Pour plus d'informations sur l'implémentation de Finalize et de Dispose pour nettoyer des ressources non managées, consultez Garbage collection. L'exemple de code suivant illustre le modèle de design de base pour implémenter Dispose. Cet exemple requiert l'espace de noms System.
' Design pattern for a base class.
Public Class Base
Implements IDisposable
' Field to handle multiple calls to Dispose gracefully.
Dim disposed as Boolean = false
' Implement IDisposable.
Public Overloads Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
Protected Overloads Overridable Sub Dispose(disposing As Boolean)
If disposed = False Then
If disposing Then
' Free other state (managed objects).
disposed = True
End If
End If
' Free your own state (unmanaged objects).
' Set large fields to null.
End Sub
Protected Overrides Sub Finalize()
' Simply call Dispose(False).
Dispose (False)
End Sub
End Class
' Design pattern for a derived class.
Public Class Derived
Inherits Base
' Field to handle multiple calls to Dispose gracefully.
Dim disposed as Boolean = false
Protected Overloads Overrides Sub Dispose(disposing As Boolean)
If disposed = False Then
If disposing Then
' Release managed resources.
disposed = True
End If
End If
' Release unmanaged resources.
' Set large fields to null.
' Call Dispose on your base class.
Mybase.Dispose(disposing)
End Sub
' The derived class does not have a Finalize method
' or a Dispose method without parameters because it inherits
' them from the base class.
End Class
// Design pattern for a base class.
public class Base: IDisposable
{
//Implement IDisposable.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// Free other state (managed objects).
}
// Free your own state (unmanaged objects).
// Set large fields to null.
}
// Use C# destructor syntax for finalization code.
~Base()
{
// Simply call Dispose(false).
Dispose (false);
}
}
// Design pattern for a derived class.
public class Derived: Base
{
protected override void Dispose(bool disposing)
{
if (disposing)
{
// Release managed resources.
}
// Release unmanaged resources.
// Set large fields to null.
// Call Dispose on your base class.
base.Dispose(disposing);
}
// The derived class does not have a Finalize method
// or a Dispose method without parameters because it inherits
// them from the base class.
}
Pour un exemple de code plus détaillé illustrant le modèle de design pour implémenter Finalize et Dispose, consultez Implémentation d'une méthode Dispose.
Personnalisation d'un nom de méthode Dispose
Un nom spécifique à un domaine est parfois plus approprié que Dispose. Par exemple, il se peut qu'une encapsulation de fichier veuille utiliser le nom de méthode Close. Dans ce cas, implémentez Dispose de manière privée et créez une méthode Close publique qui appelle Dispose. L'exemple de code suivant illustre ce modèle. Vous pouvez remplacer Close par un nom de méthode plus approprié à votre domaine. Cet exemple requiert l'espace de noms System.
' Do not make this method overridable.
' A derived class should not be allowed
' to override this method.
Public Sub Close()
' Call the Dispose method with no parameters.
Dispose()
End Sub
// Do not make this method virtual.
// A derived class should not be allowed
// to override this method.
public void Close()
{
// Call the Dispose method with no parameters.
Dispose();
}
Finalize
Les règles suivantes mettent en avant les indications d'utilisation pour la méthode Finalize :
Implémentez uniquement Finalize sur des objets qui nécessitent une finalisation. Des coûts de performance sont liés aux méthodes Finalize.
Si vous avez besoin d'une méthode Finalize, vous pouvez envisager d'implémenter IDisposable pour permettre à des utilisateurs de votre classe d'éviter le coût de l'appel à la méthode Finalize.
Ne rendez pas la méthode Finalize plus visible. Elle doit être protected, et non public.
La méthode Finalize d'un objet doit libérer toute ressource externe dont il dispose. De plus, une méthode Finalize ne doit libérer que les ressources conservées par l'objet. La méthode Finalize ne doit référencer aucun autre objet.
N'appelez pas directement une méthode Finalize sur un objet autre que la classe de base de l'objet. Cette opération n'est pas valide dans le langage de programmation C#.
Appelez la méthode Finalize de la classe de base à partir de la méthode Finalize d'un objet.
Notes
La méthode Finalize de la classe de base est appelée automatiquement avec la syntaxe du destructeur C# et C++.
Dispose
Les règles suivantes décrivent les instructions d'utilisation de la méthode Dispose :
Implémentez le modèle de design Dispose sur un type encapsulant des ressources qui doivent être explicitement libérées. Les utilisateurs peuvent libérer des ressources externes en appelant la méthode Dispose publique.
Implémentez le modèle de design Dispose sur un type de base qui comporte généralement des types dérivés rattachés à des ressources, même si le type de base n'y est pas rattaché. Si le type de base dispose d'une méthode Close, cela signifie souvent qu'il faut implémenter Dispose. Dans ces cas-là, n'implémentez pas de méthode Finalize sur le type de base. Finalize doit être implémentée dans tout type dérivé qui introduit des ressources nécessitant un nettoyage.
Libérez toute ressource pouvant être supprimée et dont un type est propriétaire dans sa méthode Dispose.
Après que la méthode Dispose a été appelée sur une instance, empêchez la méthode Finalize de s'exécuter en appelant la méthode GC.SuppressFinalize. L'exécution de tâches dans Finalize qui ne sont pas couvertes par Dispose constitue l'exception à cette règle mais cette situation se présente rarement.
Appelez la méthode Dispose de la classe de base si elle implémente IDisposable.
Ne supposez pas que la méthode Dispose sera appelée. Les ressources non managées détenues par un type doivent aussi être libérées dans une méthode Finalize au cas où la méthode Dispose n'est pas appelée.
Levez une exception ObjectDisposedException à partir des méthodes d'instance sur ce type (autres que Dispose) quand des ressources sont déjà supprimées. Cette règle ne s'applique pas à la méthode Dispose car elle doit pouvoir être appelée plusieurs fois sans lever d'exception.
Propagez les appels à Dispose par le biais de la hiérarchie de types de base. La méthode Dispose doit aussi libérer toutes les ressources détenues par cet objet et tout objet détenu par cet objet. Par exemple, vous pouvez créer un objet tel que TextReader qui détient Stream et Encoding, lesquels sont créés par TextReader sans que l'utilisateur ne le sache. De plus, Stream et Encoding peuvent tous deux obtenir des ressources externes. Quand vous appelez la méthode Dispose sur TextReader, celui-ci doit à son tour appeler Dispose sur Stream et sur Encoding, provoquant la libération de leurs ressources externes.
Vous devez envisager de rendre un objet inutilisable après l'appel à sa méthode Dispose. Il est difficile d'implémenter le modèle consistant à recréer un objet déjà supprimé.
Autorisez plusieurs appels à une méthode Dispose sans levée d'exception. La méthode ne doit pas réagir après le premier appel.
Portions Copyright 2005 Microsoft Corporation. Tous droits réservés.
Portions Copyright Addison-Wesley Corporation. Tous droits réservés.
Pour plus d'informations sur les instructions de conception, consultez le livre « Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries » de Krzysztof Cwalina et Brad Abrams, publié par Addison-Wesley, 2005.
Voir aussi
Référence
IDisposable.Dispose Method
Object.Finalize Method
Autres ressources
Instructions de conception pour le développement de bibliothèques de classes
Garbage collection
Modèles de design