Durée de vie d'un objet : création et destruction des objets (Visual Basic)
Une instance d'une classe, d'un objet est créée à l'aide du mot clé New. Des tâches d'initialisation doivent souvent être effectuées sur les nouveaux objets avant qu'ils ne soient utilisés. Les tâches d'initialisation courantes comprennent l'ouverture de fichiers, la connexion à des bases de données et la lecture de valeurs de clés de Registre. Visual Basic contrôle l'initialisation des nouveaux objets à l'aide de procédures appelées constructeurs (méthodes spéciales permettant de contrôler l'initialisation).
Lorsqu'un objet abandonne la portée, il est libéré par le Common Language Runtime (CLR). Visual Basic contrôle la libération des ressources système à l'aide de procédures nommées destructeurs. Les constructeurs et les destructeurs prennent en charge ensemble la création de bibliothèques de classe robustes et prévisibles.
Utilisation de constructeurs et de destructeurs
Les constructeurs et les destructeurs régissent la création et la destruction des objets. Les procédures Sub New et Sub Finalize de Visual Basic initialisent et détruisent des objets ; elles remplacent les méthodes Class_Initialize et Class_Terminate utilisées dans Visual Basic 6.0 et les versions antérieures.
Sub New
Le constructeur Sub New ne peut s'exécuter qu'une seule fois lorsqu'une classe est créée. Il ne peut être appelé explicitement qu'à partir de la première ligne de code d'un autre constructeur de la même classe ou d'une classe dérivée. En outre, le code dans la méthode Sub New s'exécute toujours avant tout autre code dans une classe. Visual Basic 2005 et les versions ultérieures créent implicitement un constructeur Sub New au moment de l'exécution si vous ne définissez pas explicitement de procédure Sub New pour une classe.
Pour créer un constructeur d'une classe, créez une procédure nommée Sub New à n'importe quel endroit de la définition de la classe. Pour créer un constructeur paramétré, spécifiez les noms et les types de données des arguments de la procédure Sub New, de la même façon que vous définiriez les arguments de toute autre procédure, comme dans le code ci-dessous :
Sub New(ByVal s As String)
Les constructeurs sont souvent surchargés, comme l'illustre le code suivant :
Sub New(ByVal s As String, i As Integer)
Quand vous définissez une classe dérivée d'une autre classe, la première ligne d'un constructeur doit constituer un appel au constructeur de la classe de base, à moins que la classe de base ne possède un constructeur accessible n'acceptant pas de paramètre. Un appel à la classe de base contenant le constructeur ci-dessus, par exemple, serait MyBase.New(s). Sinon, MyBase.New est facultatif, et le runtime Visual Basic l'appelle implicitement.
Après avoir écrit le code qui appelle le constructeur de l'objet parent, vous pouvez ajouter du code d'initialisation supplémentaire à la procédure Sub New. Sub New peut accepter des arguments en cas d'appel en tant que constructeur paramétrable. Ces paramètres sont passés à partir de la procédure qui appelle le constructeur, comme dans Dim AnObject As New ThisClass(X).
Sub Finalize
Avant de libérer des objets, le CLR appelle automatiquement la méthode Finalize pour les objets qui définissent une procédure Sub Finalize. La méthode Finalize peut contenir du code qui doit s'exécuter juste avant la destruction d'un objet, par exemple du code pour la fermeture de fichiers et l'enregistrement d'informations d'état. L'exécution de la méthode Sub Finalize réduit légèrement les performances, c'est pourquoi vous ne devez exécuter une méthode Sub Finalize que si vous devez libérer des objets explicitement.
Notes
Le garbage collector du CLR ne peut pas supprimer les objets non managés, objets que le système d'exploitation exécute directement, en dehors de l'environnement du CLR. Il en est ainsi parce que les différents objets non managés doivent être supprimés de différentes façons. Ces informations ne sont pas directement associées à l'objet non managé ; elles doivent être recherchées dans la documentation de l'objet. Une classe qui utilise des objets non managés doit les supprimer dans sa méthode Finalize.
Le destructeur Finalize est une méthode protégée qui peut être appelée uniquement à partir de la classe à laquelle elle appartient ou de classes dérivées. Le système appelle automatiquement Finalize lors de la destruction d'un objet, c'est pourquoi il ne faut pas appeler explicitement Finalize depuis l'extérieur de l'implémentation Finalize d'une classe dérivée.
Contrairement à Class_Terminate, qui s'exécute dès qu'un objet n'a aucune valeur, un délai est généralement constaté entre le moment où un objet passe hors de portée et le moment où Visual Basic appelle le destructeur Finalize. Visual Basic 2005 et versions ultérieures autorisent un deuxième type de destructeur, Dispose, qui peut être appelé explicitement à tout moment pour diffuser immédiatement des ressources.
Notes
Un destructeur Finalize ne doit pas lever d'exceptions, parce qu'elles ne peuvent pas être gérées par l'application et peuvent provoquer l'arrêt de cette dernière.
Fonctionnement des méthodes New et Finalize dans une hiérarchie de classe
À chaque fois qu'une instance de classe est créée, le Common Language Runtime (CLR) essaie d'exécuter une procédure nommée New, si elle existe dans cet objet. New est un type de procédure appelé constructor, utilisé pour initialiser de nouveaux objets avant que tout autre code s'exécute dans un objet. Un constructeur New peut être utilisé pour ouvrir des fichiers, se connecter à des bases de données, initialiser des variables et réaliser d'autres tâches qui doivent être effectuées avant qu'un objet ne soit utilisé.
Lorsqu'une instance d'une classe dérivée est créée, le constructeur Sub New de la classe de base s'exécute premièrement, suivi des constructeurs des classes dérivées. En effet, la première ligne de code d'un constructeur Sub New utilise la syntaxe MyBase.New()pour appeler le constructeur de la classe situé immédiatement au-dessus de celle-ci dans la hiérarchie de classe. Le constructeur Sub New est ensuite appelé pour chaque classe dans la hiérarchie de classe jusqu'à ce que le constructeur de la classe de base soit atteint. À ce stade, le code du constructeur de la classe de base s'exécute, suivi du code de chacun des constructeurs de toutes les classes dérivées ; le code des classes plus dérivées s'exécute en dernier lieu.
Lorsqu'un objet n'est plus nécessaire, le CLR appelle la méthode Finalize pour cet objet avant de libérer sa mémoire. La méthode Finalize est appelée un destructor car elle effectue des tâches de nettoyage, telles qu'un enregistrement d'informations d'état, une fermeture de fichiers et des connexions à des bases de données, ainsi que d'autres tâches qui doivent être effectuées avant de libérer l'objet.
Interface IDisposable
Les instances de classe contrôlent souvent les ressources non managées par le CLR, comme les handles Windows et les connexions aux bases de données. Ces ressources doivent être supprimées dans la méthode Finalize de la classe, afin qu'elles soient libérées lorsque l'objet est détruit par le garbage collector. Toutefois, le garbage collector ne détruit les objets que si le CLR requiert plus de mémoire disponible. Cela signifie que les ressources ne peuvent pas être libérées tant que l'objet n'abandonne pas la portée.
Pour augmenter le garbage collection, vos classes peuvent fournir un mécanisme pour gérer activement des ressources système si elles implémentent l'interface IDisposable. IDisposable a une méthode, Dispose, que les clients doivent appeler après avoir utilisé un objet. Vous pouvez employer la méthode Dispose pour libérer immédiatement des ressources et effectuer des tâches comme la fermeture de fichiers et les connexions à une base de données. Contrairement au destructeur Finalize, la méthode Dispose n'est pas appelée automatiquement. Les clients d'une classe doivent appeler explicitement Dispose pour pouvoir libérer immédiatement des ressources.
Implémentation de l'interface IDisposable
Une classe qui implémente l'interface IDisposable doit inclure les sections de code ci-dessous :
un champ permettant de vérifier la suppression ou non de l'objet :
Protected disposed As Boolean = False
surcharge du Dispose qui libère les ressources de la classe. Cette méthode doit être appelée par les méthodes Dispose et Finalize de la classe de base :
Protected Overridable Sub Dispose(ByVal disposing As Boolean) If Not Me.disposed Then If disposing Then ' Insert code to free managed resources. End If ' Insert code to free unmanaged resources. End If Me.disposed = True End Sub
une implémentation de Dispose qui contient uniquement le code suivant :
Public Sub Dispose() Implements IDisposable.Dispose Dispose(True) GC.SuppressFinalize(Me) End Sub
une substitution de la méthode Finalize qui contient uniquement le code suivant :
Protected Overrides Sub Finalize() Dispose(False) MyBase.Finalize() End Sub
Dérivation d'une classe qui implémente IDisposable
Une classe qui dérive d'une classe de base implémentant l'interface IDisposable n'a pas besoin de substituer chacune des méthodes de base à moins qu'elle n'utilise des ressources supplémentaires qui doivent être supprimées. Dans cette situation, la classe dérivée doit substituer la méthode Dispose(disposing) de la classe de base pour supprimer les ressources de la classe dérivée. Cette substitution doit appeler la méthode Dispose(disposing) de la classe de base.
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If Not Me.disposed Then
If disposing Then
' Insert code to free managed resources.
End If
' Insert code to free unmanaged resources.
End If
MyBase.Dispose(disposing)
End Sub
Une classe dérivée ne doit pas substituer les méthodes Dispose et Finalize de la classe de base. Lorsque ces méthodes sont appelées à partir d'une instance de la classe dérivée, l'implémentation de la classe de base de ces méthodes appelle la substitution de la classe dérivée de la méthode Dispose(disposing).
Garbage collection et destructeur Finalize
Le .NET Framework utilise le système garbage collection par traçage des références pour libérer régulièrement les ressources inutiles. Visual Basic 6.0 et les versions antérieures géraient les ressources à l'aide d'un système différent, appelé décompte de références. Bien que ces deux systèmes exécutent la même fonction automatiquement, un certain nombre de différences importantes les distinguent.
Le CLR détruit régulièrement les objets quand le système détermine que ces objets sont devenus inutiles. Les objets sont libérés plus vite en cas de pénurie des ressources système et moins vite dans le cas contraire. Le délai qui sépare le moment où la portée d'un objet se termine et le moment où le CLR libère celui-ci implique qu'il est désormais impossible de déterminer avec précision l'instant où l'objet sera détruit, alors que cela était possible en Visual Basic 6.0 et les versions précédentes. Dans une telle situation, l'on dit que les objets ont une durée de vie non déterminable. Dans la plupart des cas, la durée de vie non déterminable ne modifie en rien la manière dont vous écrivez les applications, à condition de ne pas oublier que le destructeur Finalize ne s'exécutera peut-être pas immédiatement au moment où la portée d'un objet s'achève.
Une autre différence entre les deux systèmes d'opérations garbage collection concerne l'utilisation de Nothing. Pour tirer profit du décompte de références dans Visual Basic 6.0 et les versions précédentes, les programmeurs ont parfois assigné Nothing aux variables objets pour libérer les références contenues dans ces variables. Si la variable contenait la dernière référence à l'objet, les ressources de ce dernier ont été libérées immédiatement. Dans les versions ultérieures de Visual Basic, si l'emploi de cette procédure présente sans doute toujours un intérêt dans certaines situations, il n'entraîne jamais la libération immédiate des ressources de l'objet référencé. Pour libérer immédiatement des ressources, utilisez la méthode Dispose de l'objet, si disponible. La seule situation qui nécessite l'attribution de Nothing à une variable est lorsque cette variable possède une durée de vie qui est longue par rapport à la période requise pour la détection des objets orphelins par le garbage collector.