web-dev-qa-db-fra.com

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

Je ne vois pas d'avantages à utiliser les événements par rapport aux délégués, mis à part le sucre syntaxique. Je me trompe peut-être, mais il semble que cet événement ne soit qu'un espace réservé pour un délégué.

Pourriez-vous m'expliquer les différences et quand utiliser lesquelles? Quels sont les avantages et les inconvénients? Notre code est fortement enraciné dans les événements et je souhaite aller au fond des choses.

Quand utiliseriez-vous des délégués pour des événements et vice versa? Veuillez indiquer votre expérience du monde réel avec les deux, par exemple dans le code de production.

105
Sasha

D'un point de vue technique, d'autres réponses ont abordé les différences.

Du point de vue de la sémantique, les événements sont des actions déclenchées par un objet lorsque certaines conditions sont remplies. Par exemple, ma classe Stock a une propriété appelée Limit et déclenche un événement lorsque le cours des actions atteint la limite. Cette notification est faite via un événement. Le fait de savoir si quelqu'un se soucie réellement de cet événement et y souscrit est au-delà des préoccupations de la classe des propriétaires.

Un délégué est un terme plus générique pour décrire une construction similaire à un pointeur en termes C/C++. Tous les délégués en .Net sont des délégués en multidiffusion. D'un point de vue sémantique, ils sont généralement utilisés comme une sorte d'entrée. En particulier, ils constituent un moyen idéal pour mettre en œuvre le modèle de stratégie . Par exemple, si je veux trier une liste d'objets, je peux fournir une stratégie de comparaison à la méthode pour indiquer à l'implémentation comment comparer deux objets.

J'ai utilisé les deux méthodes dans le code de production. Des tonnes de mes objets de données avertissent lorsque certaines propriétés sont remplies. Dans l'exemple le plus simple, chaque fois qu'une propriété est modifiée, un événement PropertyChanged est déclenché (voir Interface INotifyPropertyChanged). J'ai utilisé des délégués dans le code pour proposer différentes stratégies permettant de transformer certains objets en chaîne. Cet exemple particulier était une liste enrichie de ToString () d'implémentations pour un type d'objet particulier à afficher aux utilisateurs.

48
Szymon Rozga

Le mot clé event est un modificateur de portée pour les délégués de multidiffusion. Les différences pratiques entre cela et la déclaration d'un délégué multicast sont les suivantes:

  • Vous pouvez utiliser event dans une interface.
  • L'accès d'invocation au délégué multidiffusion est limité à la classe déclarante. Le comportement est comme si le délégué était privé pour l'invocation. Aux fins de l’affectation, l’accès est spécifié par un modificateur d’accès explicite (par exemple, public event).

Par intérêt, vous pouvez appliquer + et - pour multidiffuser des délégués, et c’est la base de la += et -= syntaxe pour l’assignation de combinaison de délégués aux événements. Ces trois extraits sont équivalents:

B = new EventHandler(this.MethodB);
C = new EventHandler(this.MethodC);
A = B + C;

Exemple 2, illustrant à la fois l’affectation directe et l’affectation combinée.

B = new EventHandler(this.MethodB);
C = new EventHandler(this.MethodC);
A = B;
A += C;

Exemple 3: syntaxe plus familière. Vous êtes probablement familiarisé avec l'attribution de null pour supprimer tous les gestionnaires.

B = new EventHandler(this.MethodB);
C = new EventHandler(this.MethodC);
A = null;
A += B;
A += C;

Comme les propriétés, les événements ont une syntaxe complète que personne n’utilise jamais. Cette:

class myExample 
{
  internal EventHandler eh;

  public event EventHandler OnSubmit 
  { 
    add 
    {
      eh = Delegate.Combine(eh, value) as EventHandler;
    }
    remove
    {
      eh = Delegate.Remove(eh, value) as EventHandler;
    }
  }

  ...
}

... Est-ce que exactement est identique à ceci:

class myExample 
{
  public event EventHandler OnSubmit;
}

Les méthodes d'ajout et de suppression sont plus apparentes dans la syntaxe plutôt standard que VB.NET utilise (pas de surcharge d'opérateur).

54
Peter Wone

Les événements sont des sucres syntaxiques. Ils sont délicieux. Quand je vois un événement, je sais quoi faire. Quand je vois un délégué, je n'en suis pas si sûr.

La combinaison d'événements et d'interfaces (plus de sucre) constitue un casse-croûte appétissant. Les délégués et les classes abstraites virtuelles pures sont beaucoup moins appétissants.

12
Sean

Les événements sont marqués comme tels dans les métadonnées. Cela permet par exemple aux concepteurs Windows Forms ou ASP.NET de distinguer les événements de simples propriétés de type délégué et de les prendre en charge de manière appropriée (en les affichant spécifiquement dans l'onglet Evénements de la fenêtre Propriétés).

Une autre différence par rapport à une propriété de type délégué est que les utilisateurs peuvent uniquement ajouter et supprimer des gestionnaires d'événements, tandis qu'avec une propriété de type délégué, ils peuvent définir la valeur:

someObj.SomeCallback = MyCallback;  // okay, replaces any existing callback
someObj.SomeEvent = MyHandler;  // not okay, must use += instead

Cela aide à isoler les abonnés aux événements: je peux ajouter mon gestionnaire à un événement et vous pouvez ajouter votre gestionnaire au même événement, sans écraser mon gestionnaire par inadvertance.

5
itowlson

Bien que les événements soient généralement implémentés avec des délégués multicast, il n’est pas nécessaire de 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:

  1. Voici un délégué. S'il vous plaît l'invoquer quand quelque chose d'intéressant arrive.
  2. Voici un délégué. Vous devriez détruire toute référence à celle-ci dès que possible (et ne plus l'appeler).

Le moyen le plus courant pour une classe de gérer un événement exposé consiste à définir un délégué multidiffusion et à ajouter/supprimer tout délégué transmis aux méthodes ci-dessus, sans qu'il soit nécessaire de le faire de cette manière. Malheureusement, l'architecture des événements ne parvient pas à faire certaines choses qui auraient rendu les approches alternatives beaucoup plus propres (par exemple, demander à la méthode d'abonnement de renvoyer un MethodInvoker, qui serait conservé par l'abonné; pour annuler l'abonnement à un événement, invoquer simplement la méthode renvoyée). sont de loin l'approche la plus courante.

4
supercat

Edit # 1 Quand utiliseriez-vous des délégués sur des événements et contre -versa? Veuillez indiquer votre expérience du monde réel 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 en tant que paramètres à des méthodes ou aux constructeurs de classes:

  • Ainsi, une méthode peut implémenter un modèle de 'méthode de modèle' simple (par exemple, les délégués Predicate et Action sont transmis aux classes de la collection générique .Net)
  • Ou pour que la classe puisse effectuer un "rappel" (généralement un rappel 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 ne doivent pas être null).

J'ai tendance à ne pas utiliser les événements; mais lorsque j'utilise des événements, je les utilise pour facultatif événements de signalisation pour zéro, un ou plusieurs clients qui pourraient être intéressés, c'est-à-dire quand il est logique qu'une classe (par exemple le System.Windows.Form class) doit exister et être exécuté, quel que soit le client ayant ajouté un gestionnaire d'événements à son événement (par exemple, l'événement 'souris vers le bas' du formulaire existe, mais facultatif si un client externe est intéressé par l'installation un gestionnaire d'événements sur cet événement).

3
ChrisW

pour comprendre les différences, vous pouvez regarder ces 2 exemples

Exemple avec délégués (Action dans ce cas qui est une sorte 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 devriez faire quelque chose comme ça

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 pouvez avoir des 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 que j'avais, je remplaçais les comportements précédents avec juste un manquant + (j'ai utilisé + au lieu de +=)

Un autre point faible est que chaque classe qui utilise votre classe Animal peut lever RaiseEvent simplement en l'appelant animal.RaiseEvent().

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

Votre classe d'animaux va changer de cette façon

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"));
       }
    }

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 contre les accès non désirés)
  2. Les événements ne peuvent pas être attribués directement. Dans ce cas, vous ne pouvez pas corriger l’erreur précédente que j’ai montrée en modifiant le comportement.
  3. Personne en dehors de votre classe ne peut soulever l'événement.
  4. Les événements peuvent être inclus dans une déclaration d'interface, alors qu'un champ ne peut pas

remarques

EventHandler est déclaré en tant que délégué suivant:

public delegate void EventHandler (object sender, EventArgs e)

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

Vous pouvez aussi utiliser EventHAndler à la place de cet exemple qui utilise EventHandler<ArgsSpecial>

se référer ici pour la documentation sur EventHandler

3
faby

Bien que je n’aie aucune raison technique, j’utilise les événements dans le code de style d’interface utilisateur, c’est-à-dire dans les niveaux supérieurs du code, et j’utilise les délégués pour une logique plus profonde dans le code. Comme je l’ai dit, vous pouvez utiliser l’un ou l’autre, mais j’estime que ce modèle d’utilisation est logiquement correct. Il permet de documenter les types de rappels et leur hiérarchie.


Edit: Je pense que la différence dans les modèles d’utilisation que j’ai, c’est que je trouve parfaitement acceptable d’ignorer les événements; ce sont des points d'ancrage, si vous avez besoin de connaître l'événement, écoutez-les, si vous ne vous en souciez pas l'événement simplement l'ignorer. C'est pourquoi je les utilise pour l'interface utilisateur, genre de style d'événement Javascript/Navigateur. Cependant, lorsque j'ai un délégué, j'attends VRAIMENT que quelqu'un gère la tâche du délégué et lève une exception s'il n'est pas traité.

2
Robert Gould

La différence entre les événements et les délégués est beaucoup plus petite que je ne le pensais auparavant. Je viens de poster une vidéo très courte sur YouTube sur le sujet: https://www.youtube.com/watch?v=el-kKK -7SB

J'espère que cela t'aides!

1
Pontus Wittenmark

Si nous n'utilisons que delegate à la place de Event, alors l'abonné a la possibilité de cloner (), invoke () le délégué lui-même, comme indiqué ci-dessous dans l'image. Ce qui n'est pas juste.

enter image description here

C'est la différence principale entre l'événement et le délégué. l'abonné n'a qu'un droit, c'est-à-dire écouter les événements

La classe ConsoleLog abonne des événements de journal via EventLogHandler

public class ConsoleLog
{
    public ConsoleLog(Operation operation)
    {
        operation.EventLogHandler += print;
    }

    public void print(string str)
    {
        Console.WriteLine("write on console : " + str);
    }
}

La classe FileLog abonne des événements de journal via EventLogHandler

public class FileLog
{
    public FileLog(Operation operation)
    {
        operation.EventLogHandler += print;
    }

    public void print(string str)
    {
        Console.WriteLine("write in File : " + str);
    }
}

La classe d'opération est la publication d'événements de journal

public delegate void logDelegate(string str);
public class Operation
{
    public event logDelegate EventLogHandler;
    public Operation()
    {
        new FileLog(this);
        new ConsoleLog(this);
    }

    public void DoWork()
    {
        EventLogHandler.Invoke("somthing is working");
    }
}
1
Narottam Goyal