mardi 16 septembre 2008

Programmation événementielle Acte II)

Les Délégués membres de classes

Un délégué peut faire partie d'une classe. La manipulation de celui-ci ne diffère guerre, si ce n'est qu'il est nécessaire de faire référence à la classe qui le possède.

Voici un exemple montrant un délégué membre d'une classe :

 

using System;

namespace DelegueTest1
{

 class Delegue 
 {
  public delegate void MDelegue();

  public static void f1()
  {
   Console.Out.WriteLine("Fonction f1");
  }

  public static void f2()
  {
   Console.Out.WriteLine("Fonction f2");
  }
 }
 
 class Test
 {
  [STAThread]
  public static void Main(string[] args)
  {
  
   Delegue d = new Delegue();
  
   Delegue.MDelegue de = new  Delegue.MDelegue(d.f1);
   de();  
  
   Console.Out.WriteLine("\n---\n");
   
   de += new  Delegue.MDelegue(d.f2);
   de();
  }
 }

}

Sortie de l'application :
Fonction f1
---
Fonction f1
Fonction f2

 

 

 

Exemple complet

 

L'exemple qui va vous etre présenter est en corélation avec l'exemple théorique présenté pour les délégué.

 

 

using System;

class PanneCarac : EventArgs
{
 private string _lieu;
 private string _description;

 public PanneCarac (string lieu, string description)
 {
  this._lieu = lieu;
  this._description = description;
 }

 public string Lieu
 {
  get
  {
   return (_lieu);
  }
 }

 public string Description
 {
  get
  {
   return (_description);
  }
 }
}


class Automobiliste
{
 public delegate void DeleguePanne (object emetteur, PanneCarac caracteristiques);

 

 public event DeleguePanne Panne;

 

 public Automobiliste()
 {
  Console.Out.WriteLine ("Un Automobiliste a été créée");
 }


 public void AppelerAide (string lieu, string description)
 {
  PanneCarac carac = new PanneCarac (lieu, description);

  if (Panne != null)
  {
   Panne(this, carac);
  }
  else
  {
   Console.Out.WriteLine ("L'Automobiliste ne sait pas qui appeler");
  }
 }
}


interface Aide
{
 void InformerPersonne (Automobiliste homme);
 void RecevoirAppel (object appelant, PanneCarac carac);
}


class Garagiste : Aide
{
 public Garagiste()
 {
 }

 public void RecevoirAppel (object appelant, PanneCarac carac)
 {
  Console.Out.WriteLine ("Le garagiste est informée que un automobiliste a besoin d'aide : ");
  Console.Out.WriteLine (carac.Lieu + " : " + carac.Description);
 }

 public void InformerPersonne(Automobiliste homme)
 {
  homme.Panne += new Automobiliste.DeleguePanne (RecevoirAppel);
 }
}

 

class Police : Aide
{
 public Police()
 {
 }

 public void RecevoirAppel (object appelant, PanneCarac carac)
 {
  Console.Out.WriteLine ("La police est informee que un automobiliste a besoin d'aide : ");
  Console.Out.WriteLine (carac.Lieu + " : " + carac.Description);
 }

 public void InformerPersonne (Automobiliste homme)
 {
  homme.Panne += new Automobiliste.DeleguePanne (RecevoirAppel);
 }
}


class Test
{
 public static void Main ()
 {
  Automobiliste homme = new Automobiliste ();

  Police police = new Police ();
  Garagiste garagiste = new Garagiste ();

  Console.Out.WriteLine ("Appuyer sur une touche pour declencher l'evenement");
   
  Console.ReadLine ();

  Console.Out.WriteLine ();
  Console.Out.WriteLine ("L'Automobiliste n'est pas informe des Aides possibles");
  homme.AppelerAide ("Autoroute A1", "Pneu creve");

  police.InformerPersonne (homme);
  Console.Out.WriteLine ();
  Console.Out.WriteLine ("L'Automobiliste est informe de l'Aide : Police");
  homme.AppelerAide ("Autoroute A1", "Pneu creve");

  garagiste.InformerPersonne (homme);
  Console.Out.WriteLine ();
  Console.Out.WriteLine ("L'Automobiliste est informe de l'Aide : Garagiste");
  homme.AppelerAide ("Autoroute A1", "Pneu creve");

 }
}

 

Sortie de l'application :

Un Automobiliste a été créée
Appuyer sur une touche pour declencher l'evenement


L'Automobiliste n'est pas informe des Aides possibles
L'Automobiliste ne sait pas qui appeler

L'Automobiliste est informe de l'Aide : Police
La police est informee que un automobiliste a besoin d'aide :
Autoroute A1 : Pneu creve

L'Automobiliste est informe de l'Aide : Garagiste
La police est informee que un automobiliste a besoin d'aide :
Autoroute A1 : Pneu creve
Le garagiste est informée que un automobiliste a besoin d'aide :
Autoroute A1 : Pneu creve

 

La première classe que nous avons créee est la classe qui permettra de créer des objets de caractéristiques d'événements. Elle hérite donc de la classe EventArgs.

La seconde classe que nous avons créee est la classe automobiliste. Cette classe sera la classe qui permettra de déclencher l'événement.
Cette classe contient donc un délégué qui permet de pointer sur une méthode gérant « la panne ».
L'instruction nouvelle ici est : public event DeleguePanne Panne;
Cette instruction crée en fait une instance de l'objet DeleguePanne. Le mot clé event permet juste de spécifier le délégué à appeler.

Dans cette même classe on retrouve la méthode a appeler en cas lorsqu'un événement se produit.

Nous créons ensuite nos classes qui vont permettre à notre automobiliste d'avoir des « récepteurs » disponibles.  Ces classes vont toutes hériter d'une interface « Aide » qui va les obliger à posséder différentes méthodes. Ici une méthode pour recevoir l'appel de l'automobiliste et une méthode pour informer l'automobiliste de son existence.

Application pratique  des événements et délégués

Un événement est un mécanisme par lequel une classe notifier son o uses clients que quelque chose est survenu. Par exemple lorsqu'on clique sur un bouton, une notification de l'événement bouton.click est envoyé vers la fenêtre qui contient le bouton. Les événements sont déclarés par le biai des délégués.

Le  Programme

L'exemple suivant est une illustration simple de la creation et de l'utilisation d'événements

using System;

 

public delegate void DivBySevenHandler(object o, DivBySevenEventArgs e);

 

public class DivBySevenEventArgs : EventArgs

{

    public readonly int TheNumber;

   

    public DivBySevenEventArgs(int num)

    {

        TheNumber = num;

    }   

   

}

 

public class DivBySevenListener

{

    public void ShowOnScreen(object o, DivBySevenEventArgs e)

    {

        Console.WriteLine(

            "divisible by seven event raised!!! the guilty party is {0}",

            e.TheNumber);

    }   

}

 

public class BusterBoy

{

    public static event DivBySevenHandler EventSeven;

   

    public static void Main()

    {

        DivBySevenListener dbsl = new DivBySevenListener();

        EventSeven += new DivBySevenHandler(dbsl.ShowOnScreen);

        GenNumbers();

    }

   

    public static void OnEventSeven(DivBySevenEventArgs e)

    {

        if(EventSeven!=null)

            EventSeven(new object(),e);

    }   

   

    public static void GenNumbers()

    {

        for(int i=0;i<99;i++)

        {

            if(i%7==0)

            {

                DivBySevenEventArgs e1 = new DivBySevenEventArgs(i);

                OnEventSeven(e1);

            }

        }       

    }

       

}

Collapse

//Output

 

F:\c#\events>1

divisible by seven event raised!!! the guilty party is 0

divisible by seven event raised!!! the guilty party is 7

divisible by seven event raised!!! the guilty party is 14

divisible by seven event raised!!! the guilty party is 21

divisible by seven event raised!!! the guilty party is 28

divisible by seven event raised!!! the guilty party is 35

divisible by seven event raised!!! the guilty party is 42

divisible by seven event raised!!! the guilty party is 49

divisible by seven event raised!!! the guilty party is 56

divisible by seven event raised!!! the guilty party is 63

divisible by seven event raised!!! the guilty party is 70

divisible by seven event raised!!! the guilty party is 77

divisible by seven event raised!!! the guilty party is 84

divisible by seven event raised!!! the guilty party is 91

divisible by seven event raised!!! the guilty party is 98

 

F:\c#\events>

 

L'objectif de ce programme est très simple: il génère des nombres aléatoires et chaque fois qu'il en génère un qui soit divisible par 7 un événement survient et le gestionnaire d'événement écris un message indiquant ce qui s'est passé tout en imprimant le nombre en question

Premièrement nous déclarons un délégué

public delegate void DivBySevenHandler(object o, DivBySevenEventArgs e);

Le délégué définit les paramètres envoyés au gestionnaire d'événement. Ainsi toute classe qui souhaite gérer cet événement doit avoir  une méthode gestionnaire qui a la même signature (valeur de retour et arguments) que le délégué. Ici comme nous le voyons le premier paramètre est un objet. Dans les cas du monde réel les gestionnaires d'événement sont passés comme référence à l'objet déclencheur. Nous ne montrerons pas cela dans cet exemple. Nous passerons simplement une nouvelle instance d'objet à cette méthode par new object(). Normalement on peut y passer la référence this. Le second paramètre est de type System.EventArgs , cette classe étant la classe de base de gestion des données relatives à des événements. On utilise donc cette classe pour envoyer des informations concernant l'événement à son gestionnaire.

Maintenant nous définissons la classe derivée d'EventArgs de la façon suivante

public class DivBySevenEventArgs : EventArgs

{

    public readonly int TheNumber;

        

    public DivBySevenEventArgs(int num)

    {

        TheNumber = num;

    }           

}

 

On voit que la méthode à un membre publique en lecture seule utilise pour stocker le nombre généré qui est divisible par 7.

A present définissons notre classe listener qui est la classe qui a besoin d'être notifiée de l'événement.

public class DivBySevenListener

{

    public void ShowOnScreen(object o, DivBySevenEventArgs e)

    {

        Console.WriteLine(

            "divisible by seven event raised!!! the guilty party is {0}",

            e.TheNumber);

    }   

}

 

On y voit la fonction ShowOnScreen qui correspond au délégué defini plus haut. On voit de quelle façon la classe DivBySevenEventArgs est passée pour imprimer le nombre divisible par 7

Examinons la classe Main (). On declare l'événement de la façon suivante:

public static event DivBySevenHandler EventSeven;

Un événement est declare comme étant une variable de type délégué. Le mot clef An event is declared like a delegate type variable, except that the keyword event precede la declaration de l'événement.

Jettons un coup d'oeil à la function qui invoque l'événement et notifie tous les clients

public static void OnEventSeven(DivBySevenEventArgs e)

{

    if(EventSeven!=null)

        EventSeven(new object(),e);

}

EventSeven sera nul si aucun client n'a déclenché le délégué lié à l'événement : il nous faut verifier qu'il n'est pas nul sinon nous courons le risque d'une levée d'exception. Si ce n'est pas nul nous invoquons l'événement en lui passant un objet basique. Après celà tous les clients seront notifiés

Examinons à présent la fonction GenNumbers()

public static void GenNumbers()

{

    for (int i=0;i<99;i++)

    {

        if(i%7==0)

        {

            DivBySevenEventArgs e1 = new DivBySevenEventArgs(i);

            OnEventSeven(e1);

        }

    }           

}

La boucle for() itère de 0 à 98 et pour chaque valeur nous testons sa divisibilité par 7. Si nous trouvons que le nombre est divisble par 7 nous créons un objet DivBySevenEventArgs  en passant en argument à son constructeur la valeur. Puis nous appelons la méthode OnEventSeven() auquel on passes l'objet DivBySevenEventArgs que nous venons juste de créer.

A present examinons la method Main()

 

public static void Main()

{

    DivBySevenListener dbsl = new DivBySevenListener();

    EventSeven += new DivBySevenHandler(dbsl.ShowOnScreen);

    GenNumbers();

}

 

Nous créons premièrement un objet DivBySevenListener puis par le biai de l'opérateur +=  nous composons un délégué dans le champ d'événement. On peut aussi utiliser -= we pour retirer un délégué d'un événement bien que ceci n'ait pas été utilisé dans ce cas précis. Une fois que tout cela est fait on appelle uniquement GenNumbers()qui fera gentiment son boulot. Chaque fois qu'un nombre divisible par 7 est généré l'événement est levé.

 

Conclusion

Nous venons de voir comment créer des événements et des gestionnaires d'événement. Un événement peut être invoqué uniquement dans la classe qui l'a declare. Ceci entraine quelques complications par rapport à l'héritage. Donc si vous devez créer votre fonction OnQuelqueChoseEvent () il vaudrait mieux la définir comme protected afin qu'elle puisse être appelé par les classes dérivées. Il est encore préférable de les rendre virtuelles afin qu'elle puisse être réécrite dans leur classe.

 



--
Alain Lompo
Excelta - Conseils et services informatiques
MCT
MCSD For Microsoft .Net
MVP Windows Systems Server / Biztalk Server
Certifié ITIL et Microsoft Biztalk Server

Aucun commentaire: