Je comprends le but des événements, en particulier dans le contexte de la création d'interfaces utilisateur. Je pense que c'est le prototype pour créer un événement:
public void EventName(object sender, EventArgs e);
Que font les gestionnaires d'événements, pourquoi sont-ils nécessaires et comment en créer un?
c#
.net
events
event-handling
Levi Campbell
la source
la source
Réponses:
Pour comprendre les gestionnaires d'événements, vous devez comprendre les délégués . En C # , vous pouvez considérer un délégué comme un pointeur (ou une référence) vers une méthode. Ceci est utile car le pointeur peut être transmis en tant que valeur.
Le concept central d'un délégué est sa signature, ou sa forme. C'est (1) le type de retour et (2) les arguments d'entrée. Par exemple, si nous créons un délégué
void MyDelegate(object sender, EventArgs e)
, il ne peut pointer que vers des méthodes qui retournentvoid
et prennent unobject
etEventArgs
. Un peu comme un trou carré et une cheville carrée. Nous disons donc que ces méthodes ont la même signature, ou forme, que le délégué.Donc, sachant comment créer une référence à une méthode, réfléchissons au but des événements: nous voulons faire exécuter du code quand quelque chose se passe ailleurs dans le système - ou "gérer l'événement". Pour ce faire, nous créons des méthodes spécifiques pour le code que nous voulons exécuter. La colle entre l'événement et les méthodes à exécuter sont les délégués. L'événement doit stocker en interne une "liste" de pointeurs vers les méthodes à appeler lorsque l'événement est déclenché. * Bien sûr, pour pouvoir appeler une méthode, nous devons savoir quels arguments lui passer! Nous utilisons le délégué comme «contrat» entre l'événement et toutes les méthodes spécifiques qui seront appelées.
Ainsi, la valeur par défaut
EventHandler
(et beaucoup comme elle) représente une forme spécifique de méthode (encore une fois, void / object-EventArgs). Lorsque vous déclarez un événement, vous dites quelle forme de méthode (EventHandler) cet événement invoquera, en spécifiant un délégué:(* Ceci est la clé des événements dans .NET et décolle la "magie" - un événement est vraiment, sous les couvertures, juste une liste de méthodes de la même "forme". La liste est stockée où l'événement vit. Quand l'événement est "déclenché", c'est vraiment juste "parcourez cette liste de méthodes et appelez chacune, en utilisant ces valeurs comme paramètres". L'affectation d'un gestionnaire d'événements est juste une manière plus jolie et plus simple d'ajouter votre méthode à cette liste de méthodes être appelé).
la source
C # connaît deux termes,
delegate
etevent
. Commençons par le premier.Déléguer
A
delegate
est une référence à une méthode. Tout comme vous pouvez créer une référence à une instance:Vous pouvez utiliser un délégué pour créer une référence à une méthode:
Maintenant que vous avez cette référence à une méthode, vous pouvez appeler la méthode via la référence:
Mais pourquoi le feriez-vous? Vous pouvez également simplement appeler
myFactory.GetInstance()
directement. Dans ce cas, vous le pouvez. Cependant, il existe de nombreux cas où vous ne souhaitez pas que le reste de l'application connaissemyFactory
ou appellemyFactory.GetInstance()
directement.Un élément évident est si vous voulez pouvoir remplacer
myFactory.GetInstance()
àmyOfflineFakeFactory.GetInstance()
partir d'un endroit central (aka modèle de méthode d'usine ).Modèle de méthode d'usine
Donc, si vous avez une
TheOtherClass
classe et qu'elle doit utiliser lemyFactory.GetInstance()
, voici à quoi ressemblera le code sans délégués (vous devrez faireTheOtherClass
connaître le type de votremyFactory
):Si vous utilisez des délégués, vous n'avez pas à exposer le type de mon usine:
Ainsi, vous pouvez donner un délégué à une autre classe à utiliser, sans leur exposer votre type. La seule chose que vous exposez est la signature de votre méthode (combien de paramètres vous avez, etc.).
"Signature de ma méthode", où ai-je entendu ça avant? O oui, les interfaces !!! les interfaces décrivent la signature d'une classe entière. Considérez les délégués comme décrivant la signature d'une seule méthode!
Une autre grande différence entre une interface et un délégué est que lorsque vous écrivez votre classe, vous n'avez pas à dire à C # "cette méthode implémente ce type de délégué". Avec les interfaces, vous devez dire "cette classe implémente ce type d'interface".
En outre, une référence de délégué peut (avec certaines restrictions, voir ci-dessous) référencer plusieurs méthodes (appelées
MulticastDelegate
). Cela signifie que lorsque vous appelez le délégué, plusieurs méthodes explicitement attachées seront exécutées. Une référence d'objet ne peut toujours faire référence qu'à un seul objet.Les restrictions pour a
MulticastDelegate
sont que la signature (méthode / délégué) ne doit pas avoir de valeur de retour (void
) et les mots clésout
etref
n'est pas utilisée dans la signature. De toute évidence, vous ne pouvez pas appeler deux méthodes qui renvoient un nombre et s'attendre à ce qu'elles renvoient le même numéro. Une fois la signature conforme, le délégué est automatiquement aMulticastDelegate
.un événement
Les événements ne sont que des propriétés (comme le get; set; les propriétés des champs d'instance) qui exposent l'abonnement au délégué d'autres objets. Cependant, ces propriétés ne prennent pas en charge get; set ;. Au lieu de cela, ils prennent en charge l'ajout; retirer;
Vous pouvez donc avoir:
Utilisation dans l'interface utilisateur (WinForms, WPF, UWP ainsi de suite)
Donc, maintenant nous savons qu'un délégué est une référence à une méthode et que nous pouvons avoir un événement pour faire savoir au monde qu'il peut nous donner ses méthodes à référencer à partir de notre délégué, et nous sommes un bouton d'interface utilisateur, alors: nous peut demander à toute personne intéressée de savoir si j'ai été cliquée, d'enregistrer sa méthode avec nous (via l'événement que nous avons exposé). Nous pouvons utiliser toutes les méthodes qui nous ont été données et les référencer par notre délégué. Et puis, nous attendrons et attendrons .... jusqu'à ce qu'un utilisateur vienne et clique sur ce bouton, nous aurons alors suffisamment de raisons d'invoquer le délégué. Et parce que le délégué fait référence à toutes ces méthodes qui nous sont données, toutes ces méthodes seront invoquées. Nous ne savons pas ce que font ces méthodes, ni nous savons quelle classe implémente ces méthodes. Tout ce qui nous importe, c'est que quelqu'un était intéressé à ce qu'on clique sur nous,
Java
Les langages comme Java n'ont pas de délégués. Ils utilisent plutôt des interfaces. La façon dont ils le font est de demander à toute personne intéressée par `` qu'on clique '', d'implémenter une certaine interface (avec une certaine méthode que nous pouvons appeler), puis de nous donner l'instance entière qui implémente l'interface. Nous gardons une liste de tous les objets implémentant cette interface et pouvons appeler leur «certaine méthode que nous pouvons appeler» chaque fois que nous cliquons.
la source
event
n'est que du sucre de syntaxe, rien de plus.Il s'agit en fait de la déclaration d'un gestionnaire d'événements - une méthode qui sera appelée lorsqu'un événement est déclenché. Pour créer un événement, vous écririez quelque chose comme ceci:
Et puis vous pouvez vous abonner à l'événement comme ceci:
Avec OnMyEvent () défini comme ceci:
Chaque fois que le
Foo
tir se déclencheMyEvent
, votreOnMyEvent
gestionnaire sera appelé.Vous n'avez pas toujours besoin d'utiliser une instance de
EventArgs
comme deuxième paramètre. Si vous souhaitez inclure des informations supplémentaires, vous pouvez utiliser une classe dérivée deEventArgs
(EventArgs
est la base par convention). Par exemple, si vous regardez certains des événements définisControl
dans WinForms ouFrameworkElement
dans WPF, vous pouvez voir des exemples d'événements qui transmettent des informations supplémentaires aux gestionnaires d'événements.la source
OnXXX
modèle de dénomination pour votre gestionnaire d'événements. (Stupidement, OnXXX est censé signifier «gérer XXX» dans MFC et «augmenter XXX» dans .net, et maintenant sa signification n'est pas claire et prête à confusion - voir ce post pour plus de détails ). Les noms préférés seraientRaiseXXX
pour déclencher des événements et /HandleXXX
ouSender_XXX
pour les gestionnaires d'événements.Voici un exemple de code qui peut vous aider:
la source
OnMaximum?.Invoke(this,new MyEventArgs("you've entered..."));
Juste pour ajouter aux bonnes réponses existantes ici - en s'appuyant sur le code de la réponse acceptée, qui utilise un
delegate void MyEventHandler(string foo)
...Parce que le compilateur connaît le type délégué de l' événement SomethingHappened , ceci:
Est totalement équivalent à:
Et les gestionnaires peuvent aussi être non enregistrée avec
-=
comme ceci:Par souci d'exhaustivité, le déclenchement de l'événement peut se faire comme ceci, uniquement dans la classe qui possède l'événement:
La copie locale du thread du gestionnaire est nécessaire pour s'assurer que l'invocation est thread-safe - sinon un thread pourrait aller désenregistrer le dernier gestionnaire de l'événement immédiatement après que nous ayons vérifié s'il l'était
null
, et nous nous amuserionsNullReferenceException
là-bas. .C # 6 a introduit une belle main courte pour ce modèle. Il utilise l'opérateur de propagation nul.
la source
Ma compréhension des événements est;
Déléguer:
Une variable pour contenir une référence à la ou aux méthodes à exécuter. Cela permet de passer des méthodes comme une variable.
Étapes pour créer et appeler l'événement:
L'événement est une instance d'un délégué
Puisqu'un événement est une instance d'un délégué, nous devons d'abord définir le délégué.
Attribuer la ou les méthodes à exécuter lors du déclenchement de l'événement ( appel du délégué )
Déclencher l'événement ( appeler le délégué )
Exemple:
la source
éditeur: où les événements se produisent. L'éditeur doit spécifier quel délégué la classe utilise et générer les arguments nécessaires, transmettre ces arguments et lui-même au délégué.
abonné: où la réponse se produit. L'abonné doit spécifier des méthodes pour répondre aux événements. Ces méthodes doivent prendre le même type d'arguments que le délégué. L'abonné ajoute ensuite cette méthode au délégué de l'éditeur.
Par conséquent, lorsque l'événement se produit dans l'éditeur, le délégué recevra certains arguments d'événement (données, etc.), mais l'éditeur n'a aucune idée de ce qui se passera avec toutes ces données. Les abonnés peuvent créer des méthodes dans leur propre classe pour répondre aux événements de la classe de l'éditeur, afin que les abonnés puissent répondre aux événements de l'éditeur.
la source
la source
Je suis d'accord avec KE50 sauf que je vois le mot-clé 'event' comme un alias pour 'ActionCollection' car l'événement contient une collection d'actions à effectuer (c'est-à-dire le délégué).
la source
Excellentes réponses techniques dans le post! Je n'ai rien à ajouter techniquement à cela.
L'une des principales raisons pour lesquelles les nouvelles fonctionnalités apparaissent dans les langues et les logiciels en général est le marketing ou la politique d'entreprise! :-) Cela ne doit pas être sous-estimé!
Je pense que cela s'applique également à certains délégués et événements! je les trouve utiles et ajoute de la valeur au langage C #, mais d'un autre côté le langage Java a décidé de ne pas les utiliser! ils ont décidé que quoi que vous résolviez avec les délégués, vous pouvez déjà le résoudre avec les fonctionnalités existantes du langage, c'est-à-dire les interfaces, par exemple
Maintenant, vers 2001, Microsoft a publié le framework .NET et le langage C # en tant que solution concurrente de Java, donc c'était bien d'avoir de NOUVELLES FONCTIONNALITÉS que Java n'a pas.
la source
J'ai récemment fait un exemple d'utilisation des événements en c # et je l'ai posté sur mon blog. J'ai essayé de le rendre aussi clair que possible, avec un exemple très simple. Au cas où cela pourrait aider quelqu'un, le voici: http://www.konsfik.com/using-events-in-csharp/
Il comprend la description et le code source (avec beaucoup de commentaires), et il se concentre principalement sur une utilisation correcte (de type modèle) des événements et des gestionnaires d'événements.
Quelques points clés:
Les événements sont comme des "sous-types de délégués", mais plus contraints (dans le bon sens). En fait, la déclaration d'un événement inclut toujours un délégué (les EventHandlers sont un type de délégué).
Les gestionnaires d'événements sont des types spécifiques de délégués (vous pouvez les considérer comme un modèle), ce qui oblige l'utilisateur à créer des événements qui ont une "signature" spécifique. La signature est au format: (expéditeur d'objet, arguments d'événement EventArgs).
Vous pouvez créer votre propre sous-classe d'EventArgs, afin d'inclure tout type d'informations que l'événement doit transmettre. Il n'est pas nécessaire d'utiliser EventHandlers lors de l'utilisation d'événements. Vous pouvez les ignorer complètement et utiliser votre propre type de délégué à leur place.
Une différence clé entre l'utilisation d'événements et de délégués est que les événements ne peuvent être invoqués qu'à partir de la classe dans laquelle ils ont été déclarés, même s'ils peuvent être déclarés publics. Il s'agit d'une distinction très importante, car elle permet à vos événements d'être exposés afin qu'ils soient "connectés" à des méthodes externes, tout en étant protégés contre les "abus externes".
la source
Une autre chose à savoir , dans certains cas, vous devez utiliser les délégués / événements lorsque vous avez besoin d'un faible niveau de couplage !
Si vous souhaitez utiliser un composant à plusieurs endroits dans l'application , vous devez créer un composant avec un faible niveau de couplage et la LOGIQUE spécifique non concernée doit être déléguée EN DEHORS de votre composant! Cela garantit que vous disposez d'un système découplé et d'un code plus propre.
En principe SOLIDE c'est le " D ", ( principe d'inversion de dépendance D ).
Aussi connu comme " IoC ", inversion de contrôle .
Vous pouvez faire " IoC " avec des événements, des délégués et DI (injection de dépendance).
Il est facile d'accéder à une méthode dans une classe enfant. Mais plus difficile d'accéder à une méthode dans une classe parent à partir d'un enfant. Vous devez transmettre la référence parent à l'enfant! (ou utilisez DI avec interface)
Les délégués / événements nous permettent de communiquer de l'enfant au parent sans référence!
Dans ce diagramme ci-dessus, je n'utilise pas Délégué / Événement et le composant parent B doit avoir une référence du composant parent A pour exécuter la logique métier non concernée dans la méthode A. (niveau de couplage élevé)
Avec cette approche, je devrais mettre toutes les références de tous les composants qui utilisent le composant B! :(
Dans ce diagramme ci-dessus, j'utilise Délégué / Événement et le composant B n'a pas à connaître A. (bas niveau de couplage)
Et vous pouvez utiliser votre composant B n'importe où dans votre application !
la source