110 votes

Différence entre les événements et les délégués et leurs applications respectives

Je ne vois pas l'avantage d'utiliser les événements plutôt que les délégués, si ce n'est le sucre syntaxique. Peut-être que je ne comprends pas bien, mais il semble que l'événement soit juste un substitut du délégué.

Pouvez-vous m'expliquer les différences et quand utiliser l'un ou l'autre ? Quels sont les avantages et les inconvénients ? Notre code est fortement ancré dans les événements, et je veux aller au fond des choses.

Quand utiliseriez-vous les délégués plutôt que les événements et vice versa ? Veuillez indiquer votre expérience réelle avec les deux, par exemple dans le code de production.

4voto

ChrisW Points 37322

Edit#1 Quand utiliseriez-vous les délégués plutôt que les événements et vice-versa ? Veuillez indiquer votre expérience réelle avec les deux, par exemple dans le code de production.

Lorsque je conçois mes propres API, je définis des délégués qui sont transmis comme paramètres aux méthodes ou aux constructeurs de classes :

  • Pour qu'une méthode puisse mettre en œuvre un modèle simple de "méthode modèle" (comme, par exemple, la méthode Predicate y Action sont transmises aux classes de collecte génériques de .Net).
  • Ou pour que la classe puisse effectuer un "callback" (généralement un callback vers une méthode de la classe qui l'a créée).

Ces les délégués sont généralement non-optionnels au moment de l'exécution (c'est-à-dire qu'il ne doit pas être null ).

J'ai tendance à ne pas utiliser les événements, mais lorsque je le fais, je les utilise pour éventuellement des événements de signalisation pour zéro, un ou plusieurs des clients qui pourrait être intéressée, c'est-à-dire lorsqu'il est logique qu'une classe (par exemple la System.Windows.Form ) doit exister et s'exécuter, qu'un client ait ou non ajouté un gestionnaire d'événement à son événement (par exemple, l'événement "souris vers le bas" du formulaire existe, mais c'est la classe en option si un client externe est intéressé par l'installation d'un gestionnaire d'événement sur cet événement).

4voto

supercat Points 25534

Bien que les événements soient généralement mis en œuvre avec des délégués multicast, rien n'oblige à les utiliser de cette manière. Si une classe expose un événement, cela signifie que la classe expose deux méthodes. Leurs significations sont, en substance, les suivantes :

  1. Voici un délégué. Veuillez l'invoquer lorsque quelque chose d'intéressant se produit.
  2. Voici un délégué. Vous devez détruire toute référence à celui-ci dès que possible (et ne plus l'appeler).

La façon la plus courante pour une classe de gérer un événement qu'elle expose est de définir un délégué multicast, et d'ajouter/supprimer tous les délégués qui sont passés aux méthodes ci-dessus, mais il n'y a aucune obligation de fonctionner de cette façon. Malheureusement, l'architecture des événements ne parvient pas à faire certaines choses qui auraient rendu les approches alternatives beaucoup plus propres (par exemple, faire en sorte que la méthode d'abonnement renvoie un MethodInvoker, qui serait conservé par l'abonné ; pour désabonner un événement, il suffit d'invoquer la méthode renvoyée), de sorte que les délégués multidiffusion sont de loin l'approche la plus courante.

4voto

faby Points 2182

Pour comprendre les différences, vous pouvez examiner ces deux exemples.

Exemple avec les délégués (dans ce cas, l'action est un type de délégué qui ne renvoie pas de valeur).

public class Animal
{
    public Action Run {get; set;}

    public void RaiseEvent()
    {
        if (Run != null)
        {
            Run();
        }
    }
}

pour utiliser le délégué vous devez faire quelque chose comme ceci

Animale animal= new Animal();
animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running") ;
animal.RaiseEvent();

ce code fonctionne bien mais vous pourriez avoir quelques points faibles.

Par exemple, si j'écris ceci

animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running");
animal.Run = () => Console.WriteLine("I'm sleeping") ;

avec la dernière ligne de code, j'ai remplacé les comportements précédents, mais il manque juste un élément + (J'ai utilisé + au lieu de += )

Un autre point faible est que chaque classe qui utilise votre Animal peut soulever RaiseEvent Je l'appelle juste animal.RaiseEvent() .

Pour éviter ces points faibles, vous pouvez utiliser events en c#.

Votre classe d'animaux changera de la manière suivante

public class ArgsSpecial :EventArgs
   {
        public ArgsSpecial (string val)
        {
            Operation=val;
        }

        public string Operation {get; set;}
   } 

 public class Animal
    {
       public event EventHandler<ArgsSpecial> Run = delegate{} //empty delegate. In this way you are sure that value is always != null because no one outside of the class can change it

       public void RaiseEvent()
       {  
          Run(this, new ArgsSpecial("Run faster"));
       }
    }

pour appeler des événements

 Animale animal= new Animal();
 animal.Run += (sender, e) => Console.WriteLine("I'm running. My value is {0}", e.Operation);
 animal.RaiseEvent();

Différences :

  1. Vous n'utilisez pas une propriété publique mais un champ public (avec les événements, le compilateur protège vos champs d'un accès non désiré)
  2. Les événements ne peuvent pas être affectés directement. Dans ce cas, vous ne pouvez pas faire l'erreur précédente que j'ai montrée en surchargeant le comportement.
  3. Personne en dehors de votre classe ne peut relancer l'événement.
  4. Les événements peuvent être inclus dans une déclaration d'interface, alors qu'un champ ne le peut pas.

notes

EventHandler est déclaré comme le délégué suivant :

public delegate void EventHandler (object sender, EventArgs e)

il prend un expéditeur (de type Object) et des arguments d'événement. L'expéditeur est nul s'il provient de méthodes statiques.

Vous pouvez également utiliser EventHAndler au lieu de cet exemple qui utilise EventHandler<ArgsSpecial>

se référer à aquí pour la documentation sur les EventHandler

3voto

Robert Gould Points 29406

Bien que je n'aie aucune raison technique de le faire, j'utilise les événements dans le code de style IU, en d'autres termes, dans les niveaux supérieurs du code, et j'utilise les délégués pour la logique qui se trouve plus profondément dans le code. Comme je l'ai dit, vous pouvez utiliser l'un ou l'autre, mais je trouve que ce modèle d'utilisation est logiquement sain. En outre, il permet de documenter les types de callbacks et leur hiérarchie.


Edit : Je pense que la différence dans les modes d'utilisation que j'ai serait que, je trouve parfaitement acceptable d'ignorer les événements, ce sont des hooks/stubs, si vous avez besoin de connaître l'événement, écoutez-les, si vous ne vous souciez pas de l'événement, ignorez-le simplement. C'est pourquoi je les utilise pour l'interface utilisateur, un peu comme les événements de Javascript/Navigateur. Cependant, lorsque j'ai un délégué, je m'attends VRAIMENT à ce que quelqu'un gère la tâche du délégué et lève une exception si elle n'est pas gérée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X