Question

Je comprends l'objectif des événements, notamment dans le cadre de la création d'interfaces utilisateur. Je pense que ceci 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 puis-je en créer un?

Était-ce utile?

La solution

Pour comprendre les gestionnaires d'événements, vous devez comprendre les délégués . Dans C # , vous pouvez considérer un délégué comme un pointeur (ou une référence). à une méthode. Ceci est utile car le pointeur peut être transmis sous forme de valeur.

Le concept central d'un délégué est sa signature, ou 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 (expéditeur d'objet, EventArgs e) , il ne peut pointer que sur les méthodes qui renvoient void et prenons un objet et EventArgs . Un peu comme un trou carré et une cheville carrée. Nous disons donc que ces méthodes ont la même signature ou la même forme que le délégué.

Maintenant que nous savons 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 lorsque 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 " des 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 être transmis! Nous utilisons le délégué comme "contrat". entre l'événement et toutes les méthodes spécifiques qui seront appelées.

Ainsi, le EventHandler par défaut (et beaucoup d’autres similaires) représente une forme de méthode spécifique (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é:

//This delegate can be used to point to methods
//which return void and take a string.
public delegate void MyEventHandler(string foo);

//This event can cause any method which conforms
//to MyEventHandler to be called.
public event MyEventHandler SomethingHappened;

//Here is some code I want to be executed
//when SomethingHappened fires.
void HandleSomethingHappened(string foo)
{
    //Do some stuff
}

//I am creating a delegate (pointer) to HandleSomethingHappened
//and adding it to SomethingHappened's list of "Event Handlers".
myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);

//To raise the event within a method.
SomethingHappened("bar");

(* Ceci est la clé des événements dans .NET et enlève le "magique" - 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ù Lorsque l'événement est "déclenché", il ne fait que parcourir cette liste de méthodes et appeler chacune d'elles en utilisant ces valeurs comme paramètres ". L'affectation d'un gestionnaire d'événement est un moyen plus simple et plus simple d'ajouter votre méthode à cette liste de méthodes à appeler).

Autres conseils

C # connaît deux termes, delegate et événement . Commençons par le premier.

Déléguer

Un délégué est une référence à une méthode. Tout comme vous pouvez créer une référence à une instance:

MyClass instance = myFactory.GetInstance();

Vous pouvez utiliser un délégué pour créer une référence à une méthode:

Action myMethod = myFactory.GetInstance;

Maintenant que vous avez cette référence à une méthode, vous pouvez appeler la méthode via la référence:

MyClass instance = myMethod();

Mais pourquoi le feriez-vous? Vous pouvez également appeler directement myFactory.GetInstance () . Dans ce cas, vous pouvez. Cependant, il existe de nombreux cas où vous ne souhaitez pas que le reste de l'application connaisse myFactory ou appelle directement myFactory.GetInstance () .

Une solution évidente est de pouvoir remplacer myFactory.GetInstance () par myOfflineFakeFactory.GetInstance () à partir d’un emplacement central (aka factory modèle de méthode ).

Modèle de méthode d'usine

Ainsi, si vous avez une classe TheOtherClass et qu'elle doit utiliser le myFactory.GetInstance () , le code ressemblera à ceci sans délégués (vous ' Vous devez informer TheOtherClass du type de votre myFactory ):

TheOtherClass toc;
//...
toc.SetFactory(myFactory);


class TheOtherClass
{
   public void SetFactory(MyFactory factory)
   {
      // set here
   }

}

Si vous utilisez des délégués, vous n'avez pas à exposer le type de mon usine:

TheOtherClass toc;
//...
Action factoryMethod = myFactory.GetInstance;
toc.SetFactoryMethod(factoryMethod);


class TheOtherClass
{
   public void SetFactoryMethod(Action factoryMethod)
   {
      // set here
   }

}

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 et tel).

"Signature de ma méthode", où ai-je entendu cela auparavant? O oui, interfaces !!! les interfaces décrivent la signature de toute une classe. Pensez aux 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".

De plus, une référence de délégué peut (avec certaines restrictions, voir ci-dessous) référencer plusieurs méthodes (appelée 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 un MulticastDelegate sont que la signature (méthode / délégué) ne doit avoir aucune valeur de retour ( void ) et les mots clés out et ref ne sont pas utilisés dans la signature. De toute évidence, vous ne pouvez pas appeler deux méthodes qui renvoient un nombre et vous attendez à ce qu'elles renvoient le même nombre. Une fois que la signature est conforme, le délégué est automatiquement un MulticastDelegate .

événement

Les événements ne sont que des propriétés (telles que les propriétés get; set; propriétés des champs d'instance) qui exposent les abonnés au délégué à partir d'autres objets. Cependant, ces propriétés ne supportent pas get; set ;. Au lieu de cela, ils soutiennent ajouter; enlever;

Vous pouvez donc avoir:

    Action myField;

    public event Action MyProperty
    {
        add { myField += value; }
        remove { myField -= value; }
    }

Utilisation dans l'interface utilisateur (WinForms, WPF, UWP, etc.)

Nous savons donc maintenant qu'un délégué est une référence à une méthode et que nous pouvons avoir un événement pour indiquer au monde qu'il peut nous indiquer ses méthodes à référencer depuis notre délégué et que nous sommes un bouton d'interface utilisateur, ensuite: nous pouvons demander à toute personne intéressée par un clic si je veux bien, nous enregistrer sa méthode (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 arrive et clique sur ce bouton, nous aurons alors suffisamment de raisons d'appeler le délégué. Et comme le délégué référence toutes les 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 quelle classe implémente ces méthodes. Tout ce qui nous importe, c’est que quelqu'un souhaitait que l'on clique sur nous et nous donne une référence à une méthode conforme à la signature souhaitée.

Java

Les langages comme Java n’ont pas de délégués. Ils utilisent des interfaces à la place. Pour ce faire, ils demandent à quiconque est intéressé par "nous sommes cliqués", d'implémenter une certaine interface (avec une certaine méthode que nous pouvons appeler), puis de nous donner toute l'instance qui implémente l'interface. Nous conservons une liste de tous les objets qui implémentent cette interface et pouvons appeler leur "certaine méthode, nous pouvons appeler" chaque fois que nous cliquons.

Voici un exemple de code qui pourrait vous aider:

using System;
using System.Collections.Generic;
using System.Text;

namespace Event_Example
{
  // First we have to define a delegate that acts as a signature for the
  // function that is ultimately called when the event is triggered.
  // You will notice that the second parameter is of MyEventArgs type.
  // This object will contain information about the triggered event.

  public delegate void MyEventHandler(object source, MyEventArgs e);

  // This is a class which describes the event to the class that receives it.
  // An EventArgs class must always derive from System.EventArgs.

  public class MyEventArgs : EventArgs
  {
    private string EventInfo;

    public MyEventArgs(string Text) {
      EventInfo = Text;
    }

    public string GetInfo() {
      return EventInfo;
    }
  }

  // This next class is the one which contains an event and triggers it
  // once an action is performed. For example, lets trigger this event
  // once a variable is incremented over a particular value. Notice the
  // event uses the MyEventHandler delegate to create a signature
  // for the called function.

  public class MyClass
  {
    public event MyEventHandler OnMaximum;

    private int i;
    private int Maximum = 10;

    public int MyValue
    {
      get { return i; }
      set
      {
        if(value <= Maximum) {
          i = value;
        }
        else 
        {
          // To make sure we only trigger the event if a handler is present
          // we check the event to make sure it's not null.
          if(OnMaximum != null) {
            OnMaximum(this, new MyEventArgs("You've entered " +
              value.ToString() +
              ", but the maximum is " +
              Maximum.ToString()));
          }
        }
      }
    }
  }

  class Program
  {
    // This is the actual method that will be assigned to the event handler
    // within the above class. This is where we perform an action once the
    // event has been triggered.

    static void MaximumReached(object source, MyEventArgs e) {
      Console.WriteLine(e.GetInfo());
    }

    static void Main(string[] args) {
      // Now lets test the event contained in the above class.
      MyClass MyObject = new MyClass();
      MyObject.OnMaximum += new MyEventHandler(MaximumReached);
      for(int x = 0; x <= 15; x++) {
        MyObject.MyValue = x;
      }
      Console.ReadLine();
    }
  }
}

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, écrivez quelque chose comme ceci:

public class Foo
{
    public event EventHandler MyEvent;
}

Ensuite, vous pouvez vous abonner à l'événement de la manière suivante:

Foo foo = new Foo();
foo.MyEvent += new EventHandler(this.OnMyEvent);

Avec OnMyEvent () défini comme suit:

private void OnMyEvent(object sender, EventArgs e)
{
    MessageBox.Show("MyEvent fired!");
}

Chaque fois que Foo se désactive MyEvent , votre gestionnaire OnMyEvent sera appelé.

Vous ne devez pas toujours utiliser une instance de EventArgs en tant que second paramètre. Si vous souhaitez inclure des informations supplémentaires, vous pouvez utiliser une classe dérivée de EventArgs ( EventArgs est la base par convention). Par exemple, si vous examinez certains des événements définis dans Control dans WinForms ou dans FrameworkElement dans WPF, vous pouvez voir des exemples d'événements qui transmettent des informations supplémentaires aux gestionnaires d'événements. .

Juste pour ajouter aux bonnes réponses existantes ici - construire sur le code de celui accepté, qui utilise un délégué void MyEventHandler (chaîne foo) ...

Le compilateur connaissant le type de délégué de l'événement SomethingHappened , voici ce qui suit:

myObj.SomethingHappened += HandleSomethingHappened;

est totalement équivalent à:

myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);

Et les gestionnaires peuvent également être non enregistrés avec - = comme ceci:

// -= removes the handler from the event's list of "listeners":
myObj.SomethingHappened -= HandleSomethingHappened;

Par souci d’intégralité, le relèvement de l’événement peut se faire de la manière suivante, uniquement dans la classe propriétaire de l’événement:

//Firing the event is done by simply providing the arguments to the event:
var handler = SomethingHappened; // thread-local copy of the event
if (handler != null) // the event is null if there are no listeners!
{
    handler("Hi there!");
}

La copie locale du thread du gestionnaire est nécessaire pour s'assurer que l'appel est thread-safe - sinon, un thread pourrait annuler l'enregistrement du dernier gestionnaire pour l'événement immédiatement après avoir vérifié si elle était null , et nous aurions un "amusement" NullReferenceException à cet endroit.

C # 6 a introduit une belle main courte pour ce motif. Il utilise l'opérateur de propagation nul.

SomethingHappened?.Invoke("Hi there!");

Je comprends les événements.

Délégué:

Une variable pour contenir une référence à une méthode / à des méthodes à exécuter. Cela permet de faire passer des méthodes comme une variable.

Étapes pour créer et appeler l'événement:

  1. L'événement est une instance d'un délégué

  2. Puisqu'un événement est une instance d'un délégué, nous devons d'abord définir le délégué.

  3. Affectez la ou les méthodes à exécuter lorsque l'événement est déclenché ( Appel du délégué )

  4. Déclenchez l'événement ( Appelez le délégué )

Exemple:

using System;

namespace test{
    class MyTestApp{
        //The Event Handler declaration
        public delegate void EventHandler();

        //The Event declaration
        public event EventHandler MyHandler;

        //The method to call
        public void Hello(){
            Console.WriteLine("Hello World of events!");
        }

        public static void Main(){
            MyTestApp TestApp = new MyTestApp();

            //Assign the method to be called when the event is fired
            TestApp.MyHandler = new EventHandler(TestApp.Hello);

            //Firing the event
            if (TestApp.MyHandler != null){
                TestApp.MyHandler();
            }
        }

    }   

}

éditeur: où les événements se produisent. L'éditeur doit spécifier le délégué que la classe utilise et générer les arguments nécessaires, puis passer ces arguments et lui-même au délégué.

abonné: où se produit la réponse. 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é reçoit des arguments d'événement (données, etc.), mais l'éditeur n'a aucune idée de ce qu'il adviendra de 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 d'éditeur, afin que les abonnés puissent répondre aux événements de l'éditeur.

//This delegate can be used to point to methods
//which return void and take a string.
public delegate void MyDelegate(string foo);

//This event can cause any method which conforms
//to MyEventHandler to be called.
public event MyDelegate MyEvent;

//Here is some code I want to be executed
//when SomethingHappened fires.
void MyEventHandler(string foo)
{
    //Do some stuff
}

//I am creating a delegate (pointer) to HandleSomethingHappened
//and adding it to SomethingHappened's list of "Event Handlers".
myObj.MyEvent += new MyDelegate (MyEventHandler);

Je suis d'accord avec KE50 sauf que je considère le mot clé 'event' comme un alias pour 'ActionCollection' car l'événement contient une collection d'actions à exécuter (le délégué par exemple).

using System;

namespace test{

class MyTestApp{
    //The Event Handler declaration
    public delegate void EventAction();

    //The Event Action Collection 
    //Equivalent to 
    //  public List<EventAction> EventActions=new List<EventAction>();
    //        
    public event EventAction EventActions;

    //An Action
    public void Hello(){
        Console.WriteLine("Hello World of events!");
    }
    //Another Action
    public void Goodbye(){
        Console.WriteLine("Goodbye Cruel World of events!");
    }

    public static void Main(){
        MyTestApp TestApp = new MyTestApp();

        //Add actions to the collection
        TestApp.EventActions += TestApp.Hello;
        TestApp.EventActions += TestApp.Goodbye;

        //Invoke all event actions
        if (TestApp.EventActions!= null){
            //this peculiar syntax hides the invoke 
            TestApp.EventActions();
            //using the 'ActionCollection' idea:
            // foreach(EventAction action in TestApp.EventActions)
            //     action.Invoke();
        }
    }

}   

}

Excellentes réponses techniques dans le post! Je n'ai rien techniquement à ajouter à cela.

L'une des principales raisons pour lesquelles les nouvelles fonctionnalités apparaissent dans les langues et dans 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 aussi dans une certaine mesure aux délégués et aux événements! Je les trouve utiles et ajoute de la valeur au langage C #, mais le langage Java a décidé de ne pas les utiliser! ils ont décidé que tout ce que vous résolvez avec des délégués peut déjà être résolu avec les fonctionnalités existantes du langage, à savoir les interfaces, par exemple.

Vers 2001, Microsoft a publié le framework .NET et le langage C # en tant que solution concurrente de Java. Il était donc bon d’avoir de NOUVELLES FONCTIONNALITÉS que Java n’a pas.

J'ai récemment créé 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 une description et un code source (avec de nombreux commentaires), et se concentre principalement sur une utilisation correcte (comme un modèle) des événements et des gestionnaires d’événements.

Certains points clés sont les suivants:

  • Les événements ressemblent à des "sous-types de délégués", seulement plus limités (dans le bon sens). En fait, la déclaration d'un événement inclut toujours un délégué (les gestionnaires d'événements 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 ayant une "signature" spécifique. La signature est au format: (expéditeur de l'objet, arguments de l'événement EventArgs).

  • Vous pouvez créer votre propre sous-classe de EventArgs afin d'inclure tout type d'informations que l'événement doit véhiculer. 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 essentielle 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 comme publics. C'est une distinction très importante car elle permet d'exposer vos événements de manière à ce qu'ils soient "connectés". aux méthodes externes tout en les protégeant contre les "utilisations abusives externes".

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top