Partage
  • Partager sur Facebook
  • Partager sur Twitter

[c#] Delegate et event

Incomprehension

Sujet résolu
    7 mars 2011 à 23:38:59

    Bonjour a tous,

    J'ai un petit logiciel de gestion d'oeuvre d'art (tp d'ecole...). La solution a 2 projets, un projet Winform pour l'ihm et un autre projet libray avec le traitement. Pour faire simple, la librairie a une classe Gallery, Curator, ArtPiece et Artist. Gallary sert en quelque sorte de controle.

    Lorsque je vends une oeuvre, je retrouve son conservateur puis lui applique sa commission. Tout ca est fonctionnel, par contre je dois creer un Event a la classe curator qui se declanchera lorsque sa commission sera modifier (soit lorsqu'une oeuvre est vendue ou lorsque qu'on la remet a 0).

    Et la...c'est le neant...Je sais que je dois creer un delegate pour gerer l'event, puis creer l'event en tant que telle puis abonner les curators a cet evement. Par contre je ne sait pas du tout par ou debuter... J'ai beau relire mes cours, des exemples ca ne veut pas!!

    Merci par avance!

    Ps: Je ne met pas de code, je ne crois pas que ce soit utile mais si vous en sentez le besoin nhesitez pas :-°
    • Partager sur Facebook
    • Partager sur Twitter
      10 mars 2011 à 22:59:24

      Tu as une jolie documentation : http://msdn.microsoft.com/fr-fr/librar [...] %29.aspx#Y570
      En gros, tu crée un évènement dans une classe (curator j'imagine dans ton cas), avec de quoi lever l'évènement :
      public class Curator
      {
          public event EventHandler CommissionChanged;
      
          public void RaiseCommissionChagend()
          {
              if(CommissionChanged != null)
              {
                  CommissionChanged(this, new EventArgs());
              }
          }
      }
      


      Ensuite, tu peux t'abonner à l'évènement "CommissionChanged" et appeler la méthode RaiseCommissionChanged pour lever l'évènement.
      • Partager sur Facebook
      • Partager sur Twitter
        11 mars 2011 à 16:15:37

        Ha merci beaucoup!

        Donc je crois que j'ai enfin capter l'idee des events (jsuis peu etre bete, mais ca ne voulait pas entrer :-°).

        Donc sur un classe simple ca donne :
        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Text;
        
        namespace ConsoleApplication1
        {
            class Program
            {
                static void Main(string[] args)
                {
                    curator cr = new curator();
                    cr.CommissionChanged += new EventHandler(cr_CommissionChanged);
        
                    cr.SetComm(150);
                    Console.ReadLine();
                }
        
                static void cr_CommissionChanged(object sender, EventArgs e)
                {
                    Console.WriteLine("EVENT");
                }
            }
        
            class curator
            {
                private int solde = 0;
        
                public event EventHandler CommissionChanged;
        
                public void RaiseCommisionChanged()
                {
                    if (CommissionChanged != null)
                        CommissionChanged(this, new EventArgs());
                }
        
                public void SetComm(int montant)
                {
                    solde += montant;
                    RaiseCommisionChanged();
                }
            }
        }
        


        Par contre, il reste toujours un point qui n'est pas clair. Dans mes livres, lors de la creation d'event, un delegate est toujours creer et je ne vois pas l'interet, a part pouvoir exiger d'autres parametres que (object sender, EventArgs e)?

        namespace ConsoleApplication1
        {
            class Program
            {
                static void Main(string[] args)
                {
                    curator cr = new curator();
                    cr.CommissionChanged += new curator.eventCurator(cr_CommissionChanged);
        
                    cr.SetComm(150);
                    Console.ReadLine();
                }
        
                static void cr_CommissionChanged(int montant)
                {
                    Console.WriteLine("solde : " + montant);
                }
            }
        
            class curator
            {
                public delegate void eventCurator(int montant);
                private int solde = 0;
        
                public event eventCurator CommissionChanged;
        
                public void RaiseCommisionChanged(int montant)
                {
                    if (CommissionChanged != null)
                        CommissionChanged(montant);
                }
        
                public void SetComm(int montant)
                {
                    solde += montant;
                    RaiseCommisionChanged(montant);
                }
            }
        }
        


        Se serait correct de faire ca? comme ca mon event peu savoir directement le montant qui a declencher l'event?

        Merci :)

        Bonne journee
        • Partager sur Facebook
        • Partager sur Twitter
          11 mars 2011 à 17:12:35

          Le code est correct, mais il y a quelques problèmes au niveau de la sémantique et il faut veiller à respecter les bonnes pratiques du framework. ^^

          Si ta classe Curator possède un événement CommissionChanged, ça suppose en général qu'elle possède une propriété Commission et que cet événement est déclenché lorsque cette propriété change de valeur. Et dans ce cas-ci, je ne sais pas si ça a beaucoup de sens de dire qu'un Curator possède à tout moment une "commission" d'une certaine valeur. Pour moi une commission est un montant qui n'existe que pendant une opération. Pense à un compte en banque par exemple: un compte possède un solde à tout moment (donc il serait parfaitement logique de donner une propriété Balance et même un événement BalanceChanged à une classe Account) mais il ne possède pas une valeur "montant du virement" à tout moment.

          Tu aurais donc plutôt quelque chose comme ceci:
          using System;
          using System.ComponentModel;
          
          namespace ConsoleApplication1
          {
              class Program
              {
                  static void Main(string[] args)
                  {
                      Curator curator = new Curator();
                      curator.PropertyChanged += curator_PropertyChanged;
                      curator.CommissionGiven += curator_CommissionGiven;
          
                      curator.GiveCommission(150);
          
                      Console.ReadLine();
                  }
          
                  static void curator_PropertyChanged(object sender, PropertyChangedEventArgs e)
                  {
                      Console.WriteLine("EVENT: PropertyChanged ({0})", e.PropertyName);
                  }
          
                  static void curator_CommissionGiven(object sender, CommissionGivenEventArgs e)
                  {
                      Console.WriteLine("EVENT: CommissionGiven ({0})", e.Amount);
                  }
              }
          
              public class Curator : INotifyPropertyChanged
              {
                  private int _balance = 0;
          
                  public event EventHandler<CommissionGivenEventArgs> CommissionGiven;
                  public event PropertyChangedEventHandler PropertyChanged;
          
                  public void RaisePropertyChanged(string propertyName)
                  {
                      if (PropertyChanged != null)
                          PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                  }
          
                  public int Balance
                  {
                      get { return _balance; }
                      private set
                      {
                          _balance = value;
                          RaisePropertyChanged("Balance");
                      }
                  }
          
                  public void GiveCommission(int amount)
                  {
                      this.Balance += amount;
          
                      if(CommissionGiven != null)
                          CommissionGiven(this, new CommissionGivenEventArgs(amount));
                  }        
              }
          
              public class CommissionGivenEventArgs : EventArgs
              {
                  public CommissionGivenEventArgs(int amount)
                  {
                      this.Amount = amount;
                  }
          
                  public int Amount { get; private set; }
              }
          }
          

          Quelques remarques sur ce code:

          - Lorsque tu veux faire en sorte qu'un objet avertisse les autres que la valeur d'une de ses propriétés a été modifiée, il est conseillé de lui faire implémenter l'interface INotifyPropertyChanged. Cette interface demande simplement à l'objet d'exposer un événement PropertyChanged ayant en paramètre le nom de la propriété modifiée. Elle est par exemple utilisée intensivement en WPF et en Silverlight pour mettre à jour automatiquement une interface utilisateur dont le contenu dépend des valeurs de telles propriétés.

          - Il est envisageable de créer un événement BalanceChanged (en plus de l'événement PropertyChanged) si c'est plus pratique pour l'utilisateur de la classe. On évitera cependant de lui donner un paramètre, car la valeur de la propriété peut être consultée directement sur l'objet Curator.

          - Lorsque tu veux créer un événement "maison", comme par exemple ici CommissionGiven, il est de bonne pratique de respecter l'interface standard des événements en créant une nouvelle classe dérivée de EventArgs pour transmettre ses éventuels paramètres (ici CommissionGivenEventArgs). Le "type" de l'événement est alors ici EventHandler<CommissionGivenEventArgs>.

          - Il est en fait déconseillé de créer de nouveaux types de delegate, le type générique EventHandler<T> étant toujours valable pour des événéments. Pour les autres cas d'utilisation des delegates on préfèrera utiliser les delegates generiques Action<T> et Func<T>. Tu noteras au passage que PropertyChangedEventHandler est un type de delegate qui n'utilise pas ces delegate génériques, parce qu'il date d'une très ancienne version du framework. Si on devait redéfinir l'interface INotifyPropertyChanged aujourd'hui on utiliserait EventHandler<PropertyChangedEventArgs> à la place.
          • Partager sur Facebook
          • Partager sur Twitter
            11 mars 2011 à 19:46:06

            Que dire... Excellente explication! Merci beaucoup!
            J'avoue que ce code montre tres clairement le fonctionnement, contrairement aux exemples tordus de mon livre de cours. Les explications rendent le tout tres clair.

            Merci encore et bonne journee

            Doum

            • Partager sur Facebook
            • Partager sur Twitter
              14 mars 2011 à 14:02:02

              Si tu veux éviter les évènements, il y a toujours la possibilité d'employer le Design Pattern Observer. Il est très facilement à mettre en place, pour peu que tu comprennes l'anglais :



              Tous les exemples ont même en C#, ce qui facilite la compréhension.

              Désolé, je suis un addic des Design Patterns...
              • Partager sur Facebook
              • Partager sur Twitter
                14 mars 2011 à 16:49:45

                @gretro : Le design pattern Observer (dans sa version présentée dans cet article) a été conçu pour des langages OO qui ne considéraient pas les fonctions comme des "citoyens de 1ère classe", ce qui signifie en particulier qu'ils n'offraient pas (ou difficilement) la possibilité de passer des fonctions/méthodes en paramètres d'autres fonctions.

                C'est notamment le cas de Java (du moins c'était le cas il y a 5 ans, ça a peut-être évolué depuis), dans lequel le pattern Observer est utilisé à toutes les sauces pour la gestion des événements. Par exemple pour gérer un clic sur un bouton on doit passer un objet implémentant ActionListener au bouton, et celui-ci appelera la méthode actionPerformed() au moment du clic.

                C'est efficace, mais ça rend le code extrêmement lourd et très peu flexible. On en arrive à créer des classes anonymes en pagaille pour gérer la multitude d'événements d'une interface.

                Heureusement, les choses se sont simplifiées pour les langages OO qui, influencés par les langages fonctionnels, ont permis le passage de fonctions par paramètres. C'est le cas du C# (et c'est une de ses grandes forces par rapport au Java). Il n'est plus nécessaire d'implémenter une interface pour pouvoir gérer un événement, il suffit de définir une méthode (éventuellement anonyme) qui respecte la signature attendue. Cela suffit à couvrir 99% des besoins, et cela rend le code beaucoup plus concis.

                On peut donc définir une variante du pattern Observer qui utiliserait des delegates plutôt que des objets Observers pour gérer les événements. Et en fait, c'est cette variante qui est mise en place par le mécanisme d'événements du C#, elle est donc directement incluse dans la syntaxe du langage ! Implémenter un pattern Observer "à la mano" ne doit donc se faire que dans des cas précis où le mécanisme built-in montre ses limites (et ils sont rares). ;)
                • Partager sur Facebook
                • Partager sur Twitter
                  15 mars 2011 à 5:58:25

                  Oh, je suis parfaitement au courant Orwell des gestionnaires d'évènements et de leurs grands avantages. Je proposais simplement une solution alternative plus "standard", disons, c'est tout. ;)

                  En plus, j,ai appris à me servir des évènements bien avant d'apprendre le Design Pattern Observer. N'empêche qu'ils restent étonnamment utiles ces petites bêtes :p !
                  • Partager sur Facebook
                  • Partager sur Twitter
                    15 mars 2011 à 12:17:06

                    Ce n'est pas parce qu'elle est plus "standard" qu'elle est préférable :) On oublie souvent hélas que chaque Design Pattern comprend dans sa définition un ensemble de préconditions indiquant si son emploi est bien adapté à son problème...

                    Cela dit si tu tiens absolument à mettre en place un pattern Observer, la façon "standard" de le faire en .Net consiste à utiliser les interfaces IObserver<T> et IObservable<T> qui sont présentes dans le framework 4 ;)
                    • Partager sur Facebook
                    • Partager sur Twitter

                    [c#] Delegate et event

                    × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                    × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                    • Editeur
                    • Markdown