Poignées vs.Ajouter un gestionnaire
-
09-06-2019 - |
Question
Y a-t-il un avantage à attacher/détacher dynamiquement des gestionnaires d’événements ?
Le détachement manuel des gestionnaires permettrait-il de garantir qu'il ne reste plus de référence à un objet supprimé ?
La solution
Il ne s'agit pas d'utiliser AddHandler ou Handles.
Si vous craignez que la référence à votre gestionnaire d'événements interfère avec le garbage collection, vous devez utiliser RemoveHandler, quelle que soit la manière dont le gestionnaire a été attaché.Dans la méthode Dispose du formulaire ou du contrôle, supprimez tous les gestionnaires.
J'ai eu des situations dans les applications Windows Forms (.NET 1.1 jours) où un gestionnaire d'événements serait appelé sur des contrôles qui n'avaient aucune autre référence à eux (et qui, à toutes fins utiles, étaient morts et j'aurais pensé qu'ils avaient été GC) -- extrêmement difficile à déboguer.
J'utiliserais RemoveHandler pour supprimer les gestionnaires des contrôles que vous n'allez pas réutiliser.
Autres conseils
Je suis presque sûr que le Handles
La clause n'est qu'un sucre syntaxique et insère un AddHandler
déclaration dans votre constructeur.J'ai testé en utilisant ce code et désactivé le framework d'application afin que le constructeur n'ait pas de choses supplémentaires :
Public Class Form1
Public Sub New()
' This call is required by the Windows Form Designer. '
InitializeComponent()
' Add any initialization after the InitializeComponent() call. '
AddHandler Me.Load, AddressOf Form1_Load
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim breakpoint As Integer = 4
End Sub
End Class
L'IL s'est terminé ainsi :
IL_0000: nop
IL_0001: ldarg.0
IL_0002: call instance void [System.Windows.Forms]System.Windows.Forms.Form::.ctor()
IL_0007: nop
IL_0008: ldarg.0
IL_0009: ldarg.0
IL_000a: dup
IL_000b: ldvirtftn instance void WindowsApplication1.Form1::Form1_Load(object,
class [mscorlib]System.EventArgs)
IL_0011: newobj instance void [mscorlib]System.EventHandler::.ctor(object,
native int)
IL_0016: call instance void [System.Windows.Forms]System.Windows.Forms.Form::add_Load(class [mscorlib]System.EventHandler)
'... lots of lines here '
IL_0047: ldarg.0
IL_0048: callvirt instance void WindowsApplication1.Form1::InitializeComponent()
IL_004d: nop
IL_004e: ldarg.0
IL_004f: ldarg.0
IL_0050: dup
IL_0051: ldvirtftn instance void WindowsApplication1.Form1::Form1_Load(object,
class [mscorlib]System.EventArgs)
IL_0057: newobj instance void [mscorlib]System.EventHandler::.ctor(object,
native int)
IL_005c: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Form::add_Load(class [mscorlib]System.EventHandler)
IL_0061: nop
IL_0062: nop
IL_0063: ret
} // end of method Form1::.ctor
Notez deux blocs de code identiques autour de IL_000b et IL_0051.Je pense que c'est juste du sucre syntaxique.
Déclarer un champ comme WithEvents
le compilateur générera automatiquement une propriété portant ce nom.Le getter renvoie la valeur d'un champ de sauvegarde.Le passeur est un peu plus compliqué.Il vérifie d'abord si le champ de sauvegarde a déjà la valeur correcte.Si c'est le cas, il sort.Sinon, si le champ de sauvegarde n'est pas nul, il émet des requêtes « RemoveHandler » pour tous ses événements vers l'objet identifié par le champ de sauvegarde.Ensuite, que le champ de sauvegarde soit non nul ou non, il le définit égal à la valeur demandée.Enfin, si la nouvelle valeur est non nulle, que l'ancienne l'était ou non, la propriété émet des requêtes "AddHandler" pour tous ses événements vers l'objet identifié par la nouvelle valeur.
À condition que l'on définisse tous les membres WithEvents d'un objet sur Nothing
avant de l'abandonner et d'éviter de manipuler les membres WithEvents dans plusieurs threads, le code d'événement généré automatiquement ne fuira pas.
Je trouve que l'attachement/détachement dynamique des gestionnaires d'événements n'est utile que lorsqu'un objet de longue durée expose des événements qui sont consommés par de nombreux objets de courte durée.Dans la plupart des autres cas, les deux objets sont éliminés à peu près en même temps et le CLR fait un travail de nettoyage suffisant tout seul.
J'attache manuellement des gestionnaires lorsque je crée manuellement des contrôles (par exemple, en créant dynamiquement une TextBox pour chaque enregistrement de base de données).Je détache manuellement les gestionnaires lorsqu'ils gèrent des choses que je ne suis pas encore prêt à gérer (peut-être parce que j'utilise les mauvais événements ?:) )
Détacher manuellement un événement peut être important pour éviter les fuites de mémoire :l'objet qui se connecte à un événement déclenché par un autre objet ne sera pas récupéré tant que l'objet qui déclenche l'événement ne sera pas récupéré.En d’autres termes, un « événement-raiser » a une forte référence à tous les « événements-auditeurs » qui lui sont connectés.
La plupart du temps, le framework s’en charge pour vous.