Domanda

Comprendo lo scopo degli eventi, specialmente nel contesto della creazione di interfacce utente. Penso che questo sia il prototipo per la creazione di un evento:

public void EventName(object sender, EventArgs e);

Cosa fanno i gestori di eventi, perché sono necessari e come posso crearne uno?

È stato utile?

Soluzione

Per comprendere i gestori di eventi, è necessario comprendere delegati . In C # , puoi pensare a un delegato come un puntatore (o un riferimento) a un metodo. Questo è utile perché il puntatore può essere passato come valore.

Il concetto centrale di un delegato è la sua firma o forma. Questo è (1) il tipo restituito e (2) gli argomenti di input. Ad esempio, se creiamo un delegato void MyDelegate (mittente oggetto, EventArgs e) , può solo puntare a metodi che restituiscono void e prendere un oggetto e EventArgs . Un po 'come un buco quadrato e un piolo quadrato. Quindi diciamo che questi metodi hanno la stessa firma o forma del delegato.

Quindi, sapendo come creare un riferimento a un metodo, pensiamo allo scopo degli eventi: vogliamo far eseguire del codice quando qualcosa accade altrove nel sistema - o "gestire l'evento". Per fare ciò, creiamo metodi specifici per il codice che vogliamo eseguire. La colla tra l'evento e i metodi da eseguire sono i delegati. L'evento deve memorizzare internamente un "elenco" dei puntatori ai metodi da chiamare quando viene generato l'evento. * Naturalmente, per poter chiamare un metodo, dobbiamo sapere quali argomenti passare ad esso! Usiamo il delegato come "contratto" tra l'evento e tutti i metodi specifici che verranno chiamati.

Quindi il EventHandler predefinito (e molti altri simili) rappresenta una forma specifica del metodo (di nuovo, void / object-EventArgs). Quando dichiari un evento, stai dicendo quale forma di metodo (EventHandler) quell'evento invocherà, specificando un delegato:

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

(* Questa è la chiave degli eventi in .NET e rimuove la "magia" - un evento è davvero, sotto le copertine, solo un elenco di metodi della stessa "forma". L'elenco è memorizzato dove l'evento dura. Quando l'evento viene "generato", è davvero solo "passare attraverso questo elenco di metodi e chiamare ciascuno, usando questi valori come parametri" L'assegnazione di un gestore di eventi è solo un modo più carino e più semplice di aggiungere il tuo metodo per questo elenco di metodi da chiamare).

Altri suggerimenti

C # conosce due termini, delegate e evento . Cominciamo con il primo.

delegato

Un delegato è un riferimento a un metodo. Proprio come puoi creare un riferimento a un'istanza:

MyClass instance = myFactory.GetInstance();

È possibile utilizzare un delegato per creare un riferimento a un metodo:

Action myMethod = myFactory.GetInstance;

Ora che hai questo riferimento a un metodo, puoi chiamarlo tramite il riferimento:

MyClass instance = myMethod();

Ma perché dovresti? Puoi anche chiamare direttamente myFactory.GetInstance () . In questo caso puoi. Tuttavia, ci sono molti casi in cui pensare a dove non vuoi che il resto dell'applicazione sia a conoscenza di myFactory o di chiamare direttamente myFactory.GetInstance () .

Uno ovvio è se vuoi essere in grado di sostituire myFactory.GetInstance () in myOfflineFakeFactory.GetInstance () da una posizione centrale (aka factory modello di metodo ).

Modello del metodo di fabbrica

Quindi, se hai una classe TheOtherClass e devi usare myFactory.GetInstance () , ecco come apparirà il codice senza delegati (tu ' dovrò informare TheOtherClass sul tipo di myFactory ):

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


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

}

Se utilizzi i delegati, non devi esporre il tipo di mia fabbrica:

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


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

}

Quindi, puoi dare un delegato ad un'altra classe da usare, senza esporre il tuo tipo a loro. L'unica cosa che stai esponendo è la firma del tuo metodo (quanti parametri hai e simili).

" Firma del mio metodo " ;, dove l'ho già sentito? O sì, interfacce !!! le interfacce descrivono la firma di un'intera classe. Pensa ai delegati come a descrivere la firma di un solo metodo!

Un'altra grande differenza tra un'interfaccia e un delegato è che quando scrivi la tua classe, non devi dire a C # "questo metodo implementa quel tipo di delegato". Con le interfacce, devi dire "questa classe implementa quel tipo di interfaccia".

Inoltre, un riferimento delegato può (con alcune restrizioni, vedere di seguito) fare riferimento a più metodi (chiamati MulticastDelegate ). Ciò significa che quando si chiama il delegato, verranno eseguiti più metodi esplicitamente collegati. Un riferimento a un oggetto può sempre fare riferimento solo a un oggetto.

Le restrizioni per un MulticastDelegate sono che la firma (metodo / delegato) non dovrebbe avere alcun valore di ritorno ( vuoto ) e le parole chiave out e ref non sono utilizzati nella firma. Ovviamente, non puoi chiamare due metodi che restituiscono un numero e aspettarti che restituiscano lo stesso numero. Una volta che la firma è conforme, il delegato diventa automaticamente un MulticastDelegate .

Evento

Gli eventi sono solo proprietà (come il get; set; proprietà ai campi dell'istanza) che espongono la sottoscrizione al delegato da altri oggetti. Queste proprietà, tuttavia, non supportano get; set ;. Al contrario, supportano add; rimuovere;

Quindi puoi avere:

    Action myField;

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

Utilizzo nell'interfaccia utente (WinForms, WPF, UWP e così via)

Quindi, ora sappiamo che un delegato è un riferimento a un metodo e che possiamo avere un evento per far sapere al mondo che possono darci i loro metodi a cui fare riferimento il nostro delegato e che siamo un pulsante dell'interfaccia utente, quindi: possiamo chiedere a chiunque sia interessato a fare clic, per registrare il loro metodo con noi (tramite l'evento che abbiamo esposto). Possiamo usare tutti quei metodi che ci sono stati dati e fare riferimento a loro dal nostro delegato. E poi, aspetteremo e aspetteremo ... fino a quando un utente arriva e fa clic su quel pulsante, quindi avremo abbastanza motivi per invocare il delegato. E poiché il delegato fa riferimento a tutti quei metodi che ci vengono forniti, tutti questi metodi verranno invocati. Non sappiamo cosa fanno questi metodi, né sappiamo quale classe implementa quei metodi. Tutto ciò che ci interessa è che qualcuno fosse interessato a fare clic su di noi e ci ha fornito un riferimento a un metodo conforme alla firma desiderata.

Java

Lingue come Java non hanno delegati. Usano invece interfacce. Il modo in cui lo fanno è chiedere a chiunque sia interessato a "farci clic", di implementare una determinata interfaccia (con un certo metodo che possiamo chiamare), quindi fornirci l'intera istanza che implementa l'interfaccia. Manteniamo un elenco di tutti gli oggetti che implementano questa interfaccia e possiamo chiamare il loro "certo metodo che possiamo chiamare" ogni volta che facciamo clic.

Ecco un esempio di codice che può aiutare:

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

Questa è in realtà la dichiarazione per un gestore di eventi - un metodo che verrà chiamato quando viene generato un evento. Per creare un evento, dovresti scrivere qualcosa del genere:

public class Foo
{
    public event EventHandler MyEvent;
}

E poi puoi iscriverti all'evento in questo modo:

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

Con OnMyEvent () definito in questo modo:

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

Ogni volta che Foo viene attivato MyEvent , verrà chiamato il gestore OnMyEvent .

Non è sempre necessario utilizzare un'istanza di EventArgs come secondo parametro. Se si desidera includere ulteriori informazioni, è possibile utilizzare una classe derivata da EventArgs ( EventArgs è la base per convenzione). Ad esempio, se guardi alcuni degli eventi definiti in Control in WinForms o FrameworkElement in WPF, puoi vedere esempi di eventi che trasmettono informazioni aggiuntive ai gestori di eventi .

Solo per aggiungere le grandi risposte esistenti qui - basandosi sul codice in quello accettato, che utilizza un delegato void MyEventHandler (string foo) ...

Poiché il compilatore conosce il tipo delegato dell'evento SomethingHappened , questo:

myObj.SomethingHappened += HandleSomethingHappened;

È totalmente equivalente a:

myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);

E i gestori possono anche essere non registrati con - = in questo modo:

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

Per completezza, sollevare l'evento può essere fatto in questo modo, solo nella classe che possiede l'evento:

//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 copia locale del gestore del thread è necessaria per assicurarsi che l'invocazione sia protetta dal thread, altrimenti un thread potrebbe andare e annullare la registrazione dell'ultimo gestore dell'evento immediatamente dopo aver verificato se fosse null e avremmo un "divertimento" NullReferenceException lì.


C # 6 ha introdotto una bella scorciatoia per questo modello. Utilizza l'operatore di propagazione null.

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

La mia comprensione degli eventi è;

Delegato:

Una variabile per contenere il riferimento al metodo / metodi da eseguire. Ciò rende possibile il passaggio di metodi come una variabile.

Passaggi per creare e chiamare l'evento:

  1. L'evento è un'istanza di un delegato

  2. Poiché un evento è un'istanza di un delegato, dobbiamo prima definire il delegato.

  3. Assegna il metodo / i metodi da eseguire quando l'evento viene generato ( Chiamata del delegato )

  4. Attiva l'evento ( Chiama il delegato )

Esempio:

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

    }   

}

editore: dove si verificano gli eventi. L'editore deve specificare quale delegato sta utilizzando la classe e generare gli argomenti necessari, passare tali argomenti e se stesso al delegato.

iscritto: dove si verifica la risposta. Il sottoscrittore deve specificare i metodi per rispondere agli eventi. Questi metodi dovrebbero assumere lo stesso tipo di argomenti del delegato. Il sottoscrittore quindi aggiunge questo metodo al delegato del publisher.

Pertanto, quando l'evento si verifica nell'editore, il delegato riceverà alcuni argomenti dell'evento (dati, ecc.), ma l'editore non ha idea di cosa accadrà con tutti questi dati. Gli abbonati possono creare metodi nella propria classe per rispondere agli eventi nella classe dell'editore, in modo che gli abbonati possano rispondere agli eventi dell'editore.

//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);

Sono d'accordo con KE50, tranne per il fatto che vedo la parola chiave "evento" come un alias per "ActionCollection" poiché l'evento contiene una raccolta di azioni da eseguire (ad es. il delegato).

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

}   

}

Grandi risposte tecniche nel post! Non ho nulla tecnicamente da aggiungere a questo.

Uno dei motivi principali per cui le nuove funzionalità appaiono in lingue e software in generale è il marketing o la politica aziendale! :-) Questo non deve essere sottovalutato!

Penso che ciò si applichi in certa misura anche ai delegati e agli eventi! li trovo utili e aggiungo valore al linguaggio C #, ma d'altra parte il linguaggio Java ha deciso di non usarli! hanno deciso che qualsiasi cosa tu stia risolvendo con i delegati puoi già risolvere con le funzionalità esistenti del linguaggio, ad es. interfacce, ad es.

Ora intorno al 2001 Microsoft ha rilasciato .NET framework e il linguaggio C # come soluzione concorrente per Java, quindi è stato bello avere NUOVE CARATTERISTICHE che Java non ha.

Di recente ho fatto un esempio di come utilizzare gli eventi in c # e l'ho pubblicato sul mio blog. Ho cercato di renderlo il più chiaro possibile, con un esempio molto semplice. Nel caso in cui possa aiutare chiunque, eccolo qui: http: //www.konsfik. com / usando-eventi-a-csharp /

Include la descrizione e il codice sorgente (con molti commenti) e si concentra principalmente su un uso corretto (simile a un modello) di eventi e gestori di eventi.

Alcuni punti chiave sono:

  • Gli eventi sono come "sottotipi di delegati", solo più vincolati (in senso buono). In effetti la dichiarazione di un evento include sempre un delegato (EventHandlers è un tipo di delegato).

  • I gestori di eventi sono tipi specifici di delegati (puoi considerarli come un modello), che costringono l'utente a creare eventi con una specifica "firma". La firma ha il formato: (mittente oggetto, eventi EventArgs).

  • È possibile creare la propria sottoclasse di EventArgs, al fine di includere qualsiasi tipo di informazione che l'evento deve trasmettere. Non è necessario utilizzare EventHandlers quando si utilizzano eventi. Puoi saltarli completamente e usare il tuo tipo di delegato al loro posto.

  • Una differenza fondamentale tra l'utilizzo di eventi e delegati è che gli eventi possono essere invocati solo all'interno della classe in cui sono stati dichiarati, anche se possono essere dichiarati pubblici. Questa è una distinzione molto importante, perché consente di esporre i tuoi eventi in modo che siano "collegati". a metodi esterni, mentre allo stesso tempo sono protetti da "uso improprio esterno".

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top