it-swarm.com.de

Grundlegendes zu Ereignissen und Ereignishandlern in C #

Ich verstehe den Zweck von Ereignissen, insbesondere im Zusammenhang mit dem Erstellen von Benutzeroberflächen. Ich denke, das ist der Prototyp für die Erstellung eines Events:

public void EventName(object sender, EventArgs e);

Was machen Eventhandler, warum werden sie benötigt und wie kann ich einen erstellen?

286
Levi Campbell

Um Event-Handler zu verstehen, müssen Sie Delegierte verstehen. In C # können Sie sich einen Delegaten als Zeiger (oder eine Referenz) auf eine Methode vorstellen. Dies ist nützlich, da der Zeiger als Wert übergeben werden kann.

Das zentrale Konzept eines Delegierten ist seine Signatur oder Form. Das ist (1) der Rückgabetyp und (2) die Eingabeargumente. Wenn wir beispielsweise einen Delegaten void MyDelegate(object sender, EventArgs e) erstellen, kann er nur auf Methoden verweisen, die void zurückgeben und object und EventArgs annehmen. Ein bisschen wie ein quadratisches Loch und ein quadratischer Stift. Wir sagen also, diese Methoden haben dieselbe Signatur oder Form wie der Delegierte.

Wenn wir wissen, wie ein Verweis auf eine Methode erstellt wird, wollen wir über den Zweck von Ereignissen nachdenken: Wir möchten, dass Code ausgeführt wird, wenn an anderer Stelle im System etwas passiert - oder "das Ereignis behandeln". Dazu erstellen wir spezifische Methoden für den Code, den wir ausführen möchten. Die Verbindung zwischen dem Ereignis und den auszuführenden Methoden sind die Delegierten. Das Ereignis muss intern eine "Liste" von Zeigern auf die Methoden speichern, die aufgerufen werden sollen, wenn das Ereignis ausgelöst wird. * Um eine Methode aufrufen zu können, müssen wir wissen, welche Argumente an sie übergeben werden müssen! Wir verwenden den Delegaten als "Vertrag" zwischen dem Ereignis und allen spezifischen Methoden, die aufgerufen werden.

Die Standardvariable EventHandler (und viele mögen es) repräsentiert eine spezifische Form der Methode (wiederum void/object-EventArgs). Wenn Sie ein Ereignis deklarieren, sagen Sie , welche Form der Methode (EventHandler) das Ereignis aufrufen soll, indem Sie einen Delegaten angeben:

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

(* Dies ist der Schlüssel zu Ereignissen in .NET und entfernt die "Magie" - ein Ereignis ist wirklich unter den Deckblättern eine Liste von Methoden derselben "Form". Die Liste wird gespeichert, wo das Ereignis lebt. Wann Das Ereignis ist "erhöht", es wird nur "diese Liste von Methoden durchlaufen und jede aufgerufen, wobei diese Werte als Parameter verwendet werden." Die Zuweisung eines Ereignishandlers ist nur eine einfachere und einfachere Möglichkeit, Ihre Methode dieser Liste von Methoden hinzuzufügen heißen).

577
Rex M

C # kennt zwei Begriffe, delegate und event. Beginnen wir mit dem ersten.

Delegieren

Ein delegate ist eine Referenz auf eine Methode. So wie Sie eine Referenz auf eine Instanz erstellen können:

MyClass instance = myFactory.GetInstance();

Sie können einen Delegaten verwenden, um einen Verweis auf eine Methode zu erstellen:

Action myMethod = myFactory.GetInstance;

Nachdem Sie nun eine Referenz auf eine Methode haben, können Sie die Methode über die Referenz aufrufen:

MyClass instance = myMethod();

Aber warum solltest du? Sie können myFactory.GetInstance() auch direkt anrufen. In diesem Fall können Sie. Es gibt jedoch viele Fälle, in denen Sie darüber nachdenken sollten, wo Sie nicht möchten, dass der Rest der Anwendung myFactory kennt oder myFactory.GetInstance() direkt anruft. 

Es liegt auf der Hand, wenn Sie myFactory.GetInstance() von einer zentralen Stelle aus in myOfflineFakeFactory.GetInstance() ersetzen wollen (aka factory method pattern ).

Fabrikmethode Muster

Wenn Sie also eine TheOtherClass-Klasse haben und die myFactory.GetInstance() verwenden müssen, wird der Code folgendermaßen ohne Delegaten aussehen (Sie müssen TheOtherClass den Typ Ihres myFactory-Objekts wissen lassen):

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


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

}

Wenn Sie Delegierte verwenden, müssen Sie den Typ meiner Fabrik nicht offenlegen:

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


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

}

So können Sie einen Delegaten einer anderen Klasse zur Verwendung geben, ohne dass Sie Ihren Typ für sie freigeben. Die einzige Sache, die Sie offenlegen, ist die Signatur Ihrer Methode (wie viele Parameter Sie haben und so). 

"Unterschrift meiner Methode", wo habe ich das schon mal gehört? O ja, Schnittstellen !!! Schnittstellen beschreiben die Signatur einer ganzen Klasse. Stellen Sie sich Delegierte als die Signatur nur einer Methode vor!

Ein weiterer großer Unterschied zwischen einer Schnittstelle und einem Delegaten besteht darin, dass Sie beim Schreiben Ihrer Klasse C # nicht sagen müssen: "Diese Methode implementiert diesen Delegatentyp". Bei Schnittstellen müssen Sie sagen "Diese Klasse implementiert diesen Typ einer Schnittstelle".

Außerdem kann eine Delegatenreferenz (mit einigen Einschränkungen, siehe unten) auf mehrere Methoden verweisen (genannt MulticastDelegate). Das bedeutet, dass beim Aufruf des Delegaten mehrere explizit angefügte Methoden ausgeführt werden. Eine Objektreferenz kann immer nur auf ein Objekt verweisen. 

Die Einschränkungen für MulticastDelegate bestehen darin, dass die Signatur (Methode/Delegat) keinen Rückgabewert (void) enthalten sollte und die Schlüsselwörter out und ref nicht in der Signatur verwendet werden. Offensichtlich können Sie nicht zwei Methoden aufrufen, die eine Nummer zurückgeben und von ihnen erwarten, dass sie dieselbe Nummer zurückgeben. Sobald die Signatur erfüllt ist, ist der Delegat automatisch ein MulticastDelegate.

Veranstaltung

Ereignisse sind nur Eigenschaften (wie get; set; Eigenschaften für Instanzfelder), die die Subskription von anderen Objekten für den Delegaten verfügbar machen. Diese Eigenschaften unterstützen jedoch nicht get, set ;. Stattdessen unterstützen sie add; Löschen;

So können Sie haben:

    Action myField;

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

Verwendung in der Benutzeroberfläche (WinForms, WPF, UWP So on)

Jetzt wissen wir, dass ein Delegierter ein Verweis auf eine Methode ist, und dass wir ein Ereignis haben können, um die Welt wissen zu lassen, dass sie uns ihre Methoden geben können, um von unserem Delegierten referenziert zu werden Ich kann jeden Interessierten fragen, ob ich angeklickt wurde, seine Methode bei uns zu registrieren (über das Ereignis, das wir gezeigt haben). Wir können alle Methoden anwenden, die uns von unserem Delegierten zur Verfügung gestellt wurden. Und dann warten wir und warten ... bis ein Benutzer kommt und auf diese Schaltfläche klickt, dann haben wir genügend Gründe, um den Delegaten aufzurufen. Und da der Delegat auf alle uns angegebenen Methoden verweist, werden alle diese Methoden aufgerufen. Wir wissen nicht, was diese Methoden tun, noch wissen wir, welche Klasse diese Methoden implementiert. Alles, was uns interessiert, ist, dass jemand daran interessiert war, dass wir angeklickt wurden, und uns einen Hinweis auf eine Methode gab, die unserer gewünschten Signatur entsprach.

Java

Sprachen wie Java haben keine Delegierten. Sie verwenden stattdessen Schnittstellen. Dies ist die Art und Weise, in der wir jeden, der sich für "uns angeklickt" interessiert, bitten, eine bestimmte Schnittstelle zu implementieren (mit einer bestimmten Methode, die wir aufrufen können) und uns dann die gesamte Instanz geben, die die Schnittstelle implementiert. Wir führen eine Liste aller Objekte, die diese Schnittstelle implementieren, und können ihre "bestimmte Methode, die wir aufrufen können" aufrufen, wenn wir angeklickt werden.

93
tofi9

Hier ist ein Codebeispiel, das helfen kann:

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

Dies ist eigentlich die Deklaration für einen Event-Handler - eine Methode, die aufgerufen wird, wenn ein Event ausgelöst wird. Um ein Ereignis zu erstellen, schreiben Sie so etwas:

public class Foo
{
    public event EventHandler MyEvent;
}

Und dann können Sie die Veranstaltung wie folgt abonnieren:

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

Mit OnMyEvent () wie folgt definiert:

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

Wenn Foo von MyEvent ausgelöst wird, wird Ihr OnMyEvent-Handler aufgerufen.

Sie müssen nicht immer eine Instanz von EventArgs als zweiten Parameter verwenden. Wenn Sie zusätzliche Informationen hinzufügen möchten, können Sie eine von EventArgs abgeleitete Klasse verwenden (EventArgs ist die Basis nach Konvention). Wenn Sie beispielsweise einige der Ereignisse betrachten, die in WinForms unter Control oder in WPF unter FrameworkElement definiert sind, können Sie Beispiele für Ereignisse sehen, die zusätzliche Informationen an die Ereignishandler übergeben.

33
Andy

Nur um die vorhandenen großartigen Antworten hinzuzufügen - aufbauend auf dem Code im akzeptierten Code, der eine delegate void MyEventHandler(string foo)... verwendet.

Da der Compiler den Delegattyp des SomethingHappened -Ereignisses kennt, gilt Folgendes:

myObj.SomethingHappened += HandleSomethingHappened;

Ist völlig gleichbedeutend mit:

myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);

Und Handler können auch unregistriert mit -= wie folgt sein:

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

Der Vollständigkeit halber kann das Erheben des Ereignisses auf diese Weise nur in der Klasse durchgeführt werden, die das Ereignis besitzt:

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

Die Thread-lokale Kopie des Handlers ist erforderlich, um sicherzustellen, dass der Aufruf threadsicher ist. Andernfalls könnte ein Thread den letzten Handler für das Ereignis sofort abmelden, nachdem wir überprüft haben, ob es null war. NullReferenceException dort.


C # 6 führte für dieses Pattern eine Nice-kurze Hand ein. Es verwendet den Nullausbreitungsoperator. 

SomethingHappened?.Invoke("Hi there!");
21
Mathieu Guindon

Ich verstehe die Ereignisse so;

Delegierter:  

Eine Variable, die eine Referenz auf die auszuführenden Methoden enthält. Dadurch können Methoden wie eine Variable umgangen werden.

Schritte zum Erstellen und Aufrufen des Ereignisses:

  1. Die Veranstaltung ist eine Instanz eines Delegierten

  2. Da ein Ereignis eine Instanz eines Delegaten ist, müssen wir zunächst den Delegierten definieren.

  3. Weisen Sie die Methode/Methoden zu, die ausgeführt werden sollen, wenn das Ereignis ausgelöst wird (Delegate aufrufen).

  4. Ereignis auslösen (Delegat anrufen)

Beispiel:

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

    }   

}
11
KE50

herausgeber: wo die Ereignisse stattfinden. Publisher sollte angeben, welcher Delegat die Klasse verwendet, und die erforderlichen Argumente generieren, diese Argumente und sich selbst an den Delegaten übergeben.

abonnent: wo die Antwort erfolgt. Der Abonnent sollte Methoden angeben, um auf Ereignisse zu reagieren. Diese Methoden sollten denselben Typ von Argumenten wie der Delegat annehmen. Der Abonnent fügt diese Methode dann dem Delegaten des Publishers hinzu.

Wenn das Ereignis im Herausgeber stattfindet, erhält der Delegat daher einige Ereignisargumente (Daten usw.), der Herausgeber hat jedoch keine Ahnung, was mit all diesen Daten geschehen wird. Abonnenten können in ihrer eigenen Klasse Methoden erstellen, um auf Ereignisse in der Klasse des Publishers zu reagieren, sodass Abonnenten auf Ereignisse des Publishers reagieren können.

3
rileyss
//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);
2
Bilgi Sayar

Ich stimme mit KE50 überein, mit der Ausnahme, dass ich das Schlüsselwort 'event' als Alias ​​für 'ActionCollection' sehe, da das Ereignis eine Sammlung von auszuführenden Aktionen (dh den Delegierten) enthält.

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

}   

}
0
user3902302

Tolle technische Antworten in der Post! Ich habe nichts technisch dazu.

Einer der Hauptgründe, warum neue Funktionen in Sprachen und Software allgemein erscheinen, ist Marketing oder Unternehmenspolitik! :-) Das darf nicht unterschätzt werden!

Ich denke, das gilt in gewissem Umfang auch für Delegierte und Veranstaltungen! Ich finde sie nützlich und wertet die C # -Sprache auf, aber andererseits hat die Java-Sprache entschieden, sie nicht zu verwenden! Sie entschieden, dass das, was Sie mit Delegierten lösen, bereits mit den vorhandenen Merkmalen der Sprache gelöst werden kann, d. h. 

Gegen 2001 hat Microsoft das .NET-Framework und die C # -Sprache als Konkurrenzlösung für Java veröffentlicht. Daher war es gut, NEUE FUNKTIONEN zu haben, die Java nicht bietet.

0
Siraf

Ich habe kürzlich ein Beispiel für die Verwendung von Ereignissen in c # erstellt und in meinem Blog veröffentlicht. Ich habe versucht, es mit einem sehr einfachen Beispiel so klar wie möglich zu machen. Falls es jemandem helfen könnte, hier ist es: http://www.konsfik.com/using-events-in-csharp/

Es enthält eine Beschreibung und einen Quellcode (mit vielen Kommentaren) und konzentriert sich hauptsächlich auf die ordnungsgemäße (vorlagenartige) Verwendung von Ereignissen und Ereignishandlern.

Einige wichtige Punkte sind:

  • Ereignisse sind wie "Subtypen von Delegierten", nur eingeschränkter (in guter Weise). Tatsächlich enthält eine Ereignisdeklaration immer einen Delegaten (EventHandler sind eine Art von Delegaten).

  • Ereignishandler sind bestimmte Arten von Delegaten (Sie können sie als Vorlage betrachten), die den Benutzer zwingen, Ereignisse mit einer bestimmten "Signatur" zu erstellen. Die Signatur hat das Format: (Objektabsender, EventArgs-Ereignisargumente).

  • Sie können Ihre eigene Unterklasse von EventArgs erstellen, um alle Arten von Informationen aufzunehmen, die das Event vermitteln muss. Es ist nicht erforderlich, EventHandler zu verwenden, wenn Ereignisse verwendet werden. Sie können sie vollständig überspringen und Ihre eigene Art von Stellvertreter an deren Stelle einsetzen.

  • Ein wesentlicher Unterschied zwischen der Verwendung von Ereignissen und Delegaten besteht darin, dass Ereignisse nur innerhalb der Klasse aufgerufen werden können, in der sie deklariert wurden, obwohl sie möglicherweise als öffentlich deklariert wurden. Dies ist eine sehr wichtige Unterscheidung, da dadurch Ihre Ereignisse offengelegt werden können, so dass sie mit externen Methoden "verbunden" sind, während sie gleichzeitig vor "externem Missbrauch" geschützt sind.

0
konsfik