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.
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.
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:
event
dans une interface.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).
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.
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.
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:
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.
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:
Predicate
et Action
sont transmis aux classes de la collection générique .Net)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).
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:
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
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é.
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!
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.
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");
}
}