Site:  Cours VB.net  
8.6 Les délégués, les évènements.

 

Super complexe? non!!

Définition.

 

Un délégué est une référence (un type référence) qui fait référence à une méthode, qui pointe sur une méthode. L'équivalent le plus proche d'un délégué dans d'autres langages est le pointeur de fonction.

On peut créer directement un délégué avec le mot Delegate et AddressOf.
Dans la gestion des évènements des contrôles on crée aussi des délégués avec Handles  et AddHandler.
A- Création d'un délégué avec 'Delegate'
Ici on ne parle pas d'évènement
On déclare un délégué (un pointeur):
Delegate Sub D()
on l'instance: on le fait pointer sur une fonction F().
Dim M As New D(AddressOf F)
Quand on utilise l'instance du délégué, cela exécute la fonction.
M() exécute la fonction F()
AddressOf permet de préciser l'adresse de la procédure F(), le pointeur vers la procédure F().
Exemple hyper simple:
Delegate Sub SimpleDelegate() 'On crée un délégué sans paramètres
Module Test
   
   Sub MaSub() 'On crée une Sub
      System.Console.WriteLine("Test")
   End Sub
   
   Sub Main()
      Dim Mondelegue As New SimpleDelegate(AddressOf MaSub) 'le délégué pointe sur la Sub
      Mondelegue()	'On utilise le délégué
   End Sub
End Module 

Il n'est pas d'un grand intérêt d'instancier un délégué pour une méthode et d'appeler ensuite immédiatement la méthode via le délégué, puisqu'il serait plus simple d'appeler directement la méthode, mais c'est un exemple.

Exemple avec une fonction et des paramètres:

Bien sur le délégué peut pointer vers une Sub ou une fonction avec des paramètres:

 

Dans une Classe MaClasse:

 

On déclare un delegate en indiquant paramètres envoyés et de retour :

Delegate Function maMethodDelegate(myInt As Integer) As [String]

 

On déclare une fonction:

Public Shared Function maMethode(myInt As Integer) As [String]

    Return myInt.ToString
End Function

 

Ici l'exemple est bête: on donne un Integer, cela retourne une String!!

 

On crée le délégué:

Dim myD As New maMethodDelegate(AddressOf maClasse.maMethode)

 

myD est maintenant un pointeur sur maClasse.maMethode.

 

Je peux utiliser myD(2) qui retournera une String "2"

 

Intérêts d'un délégué par rapport à une fonction?

 

Un délégué est un type référence qui fait référence à une méthode Shared d'un type ou à une méthode d'instance d'un objet.

Un délégué peut référencer à la fois des méthodes de classe (static) et des méthodes d'instance. Lorsque le délégué référence une méthode d'instance, il stocke non seulement une référence au point d'entrée de la méthode, mais également une référence à l'instance de classe pour laquelle la méthode est appelée. Contrairement aux pointeurs fonction, les délégués sont orientés objet, de type sécurisé et fiables.

 

Voici un exemple de Microsoft utilisant un délégué avec les 2 types de méthodes.

Imports System

Public Class SamplesDelegate

' Declares un delegate à partir d'une méthode qui accepte un paramètre integer et retourne une String.
Delegate Function myMethodDelegate(myInt As Integer) As [String]

' Definir les méthodes.
Public Class mySampleClass

' Definir une méthode d'instance.
Public Function myStringMethod(myInt As Integer) As [String]
If myInt > 0 Then
Return "positive"
End If
If myInt < 0 Then
Return "negative"
End If
Return "zero"
End Function 'myStringMethod


' Definir une méthode de classe.
Public Shared Function mySignMethod(myInt As Integer) As [String]
If myInt > 0 Then
Return "+"
End If
If myInt < 0 Then
Return "-"
End If
Return ""
End Function 'mySignMethod
End Class 'mySampleClass


'Utilisation du délégué

Public Shared Sub Main()

' Instanciation de délégué pour chaque méthode.
Dim mySC As New mySampleClass()
Dim myD1 As New myMethodDelegate(AddressOf mySC.myStringMethod)
Dim myD2 As New myMethodDelegate(AddressOf mySampleClass.mySignMethod)

'Utilisation des délégués.
Console.WriteLine("{0} is {1}; use the sign ""{2}"".", 5, myD1(5), myD2(5))
Console.WriteLine("{0} is {1}; use the sign ""{2}"".", - 3, myD1(- 3), myD2(- 3))
Console.WriteLine("{0} is {1}; use the sign ""{2}"".", 0, myD1(0), myD2(0))

End Sub 'Main

End Class 'SamplesDelegate



'Le code produit les sorties suivantes:
'
'5 is positive; use the sign "+".
'-3 is negative; use the sign "-".
'0 is zero; use the sign "".

 

Les membres d'un délégué sont les membres hérités de la classe System.Delegate. Un délégué contient également un ensemble de constructeurs et de méthodes définis par le système. 

L'utilité des délégués réside dans leur anonymat. L'exemple suivant illustre une méthode MultiCall qui appelle de façon répétitive une instance SimpleDelegate :

   Sub MultiCall(d As SimpleDelegate, count As Integer)
      Dim i As Integer
      For i = 0 To count - 1
         d()
      Next i
   End Sub

Pour la méthode MultiCall, l'identité de la méthode cible de SimpleDelegate n'a pas d'importance, pas plus que l'accessibilité qu'a cette méthode, ni le fait qu'il s'agisse d'une méthode Shared ou non partagée.

La seule chose qui importe est que la signature de la méthode soit compatible avec SimpleDelegate.

B-Délégué et appel asynchrone

Quand une procédure A appelle une autre procédure B, cela se passe de manière synchrone: pendant l'exécution de la procédure B, la procédure A est en attente, elle se poursuit après le retour de la procédure B.

Si vous appelé la procédure B à partir d'un délégué avec BeginInvoke , le fonctionnement sera asynchrone, c'est à dire que les 2 procédures se dérouleront en parallèle.

Si la méthode BeginInvoke est appelée, le Common Language Runtime mettra la demande en file d'attente et le retour à l'appelant sera immédiat. La méthode cible sera appelée sur un autre thread . Le thread d'origine, qui a soumis la demande, est libre de poursuivre en parallèle son exécution. La méthode BeginInvoke est donc utilisée pour établir l'appel asynchrone. Elle possède les mêmes paramètres que la méthode à exécuter de façon asynchrone, plus deux paramètres. Le premier paramètre supplémentaire sert quand l'appel asynchrone se termine (voir plus bas); le second paramètre supplémentaire sert à fournir un objet quelconque.

Delegate Function myDelegate(myInt As Integer) As [String]

Dim ta As New myMethodDelegate(AddressOf MyMethode)

ta.BeginInvoque(2 ,Nothing, Nothing)  'On met les 2 paramètres supplémentaire à Nothing pour le momment.

Ici on a exécuté dans un autre thread, en parallèle, MyMethode.

On reprend l'exemple bête, d'une fonction qui transforme un integer en String.

Public Shared Function myMethode(myInt As Integer) As [String]

    Return myInt.ToString
End Function

 

 

Le problème est de savoir quand MyMethode se termine et éventuellement récupérer les résultats de MyMethode.

Là intervient le premier paramètre supplémentaire: il est chargé d'indiquer l'adresse de la procédure à exécuter lorsque l'opération asynchrone se termine.

ta.BeginInvoque(2 ,AdresssOf AfficheResultat, Nothing)

Ici quand ta se termine cela exécute la procédure AfficheResultat.

Il faut donc créer une procédure AfficheResultat avec comme paramètre une variable de type IAsyncResult qui permet de récupérer le résultat.

Sub AfficheResultat (By Val ia As IAsyncResult)

Dim Resultat As String

Resultat= ta.EndInvoke(ia)

End Sub

La méthode EndInvoke est utilisée pour obtenir la valeur de retour de la fonction et les paramètres de sortie.

On peut aussi  'surveiller' si ta est terminé et récupérer le résultat:

Il faut déclarer une variable de type IAsyncResult:

Dim ia As IAsyncResult

Quand on instance ta avec BeginInvoke cela retourne un IAsyncResult si on écrit:

ia= ta.BeginInvoque(2 ,Nothing, Nothing)

Il suffit de tester ia avec ia.IsCompleted pour voir si ta est terminé; a titre d'exemple créons un bouton testant si la procédure asynchrone est terminée.

Private Sub Button1_Click(..)

    Dim Resultat As String

    If ia.IsCompleted Then

        Resultat=ta.EndInvoke(ia)

    Else

        MsgBox(" Traitement en cours..")

    End If

End Sub

Il faut cliquer sur le bouton à intervalle régulier pour voir si ta est terminé. (L'exemple est débile car tout cela à de l'intérêt que si la procédure asynchrone est très longue.

 

C-Délégué et évènement:

Les délégués sont utilisés pour lier les événements aux méthodes servant à les gérer. Lorsque l'événement se produit, le délégué appelle la méthode liée.

Délégué crée automatiquement par Visual Basic .NET

Dans le cas d'événements associés à des formulaires ou contrôles, Visual Basic .NET crée automatiquement un gestionnaire d'événements et l'associe à un événement.

Effectivement, en mode design, lorsque vous double-cliquez sur un bouton de commande dans un formulaire, Visual Basic .NET crée automatiquement un gestionnaire d'événements vide et une variable WithEvents pour le bouton de commande, comme dans le code suivant:
 

Dans la Region " Code généré par le Concepteur Windows Form " il y a:

Friend WithEvents Button1 As System.Windows.Forms.Button
 

Protected Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click


End Sub

WithEvents indique que l'objet Button1  a des évènements.

Le terme Handles  provoque l'association d'un événement (Button1.Click situé après Handles) à un gestionnaire d'événements ( la Sub Button1_Click; la Sub pourrait d'ailleurs se nommer différemment, cela n'a pas d'importance).

L'association d'événements aux gestionnaires d'événements se fait au moment de la compilation et ne peut pas être modifiée.

 

Lorsque vous cliquez sur un Button1 cela déclenche bien la Sub Button1_Click.
 

Délégué et évènements crées par vous:

On reprend les mêmes concepts que dans le chapitre sur le création de contrôles par code.

Lorsque vous crée par code de toute pièce des contrôles, vous pouvez faire de même avec Handles:

Déclaration dans la partie déclaration du module(en haut):

Private WithEvents Button1 As  New Button

Me.Controls.Add(Button1)

Sub OnClique ( sender As Object, EvArg As EventArgs) Handles Button1.Click

End Sub 

La aussi, l'association d'événements aux gestionnaires d'événements se fait au moment de la compilation et ne peut pas être modifiée. On a bien crée un délégué.

 

Vous pouvez aussi utiliser la méthode AddHandler:

Dim TB As New System.Windows.Forms.TextBox

Me.Controls.Add(TB)

AddHandler TB.Keyup, AddressOf TextboxKeyup.

 

Sub TextboxKeyup.(ByVal sender As Object, ByVal e As KeyEventArgs)

...

End Sub

AddHandler permet donc d'associer à l'évènement TB.Keyup la Sub TextboxKeyup. On a bien crée un délégué.

le mot clé addhandler permet d'associer un événement à une procédure au moment de l'exécution et peut être annulé par RemoveHandler.