Zrozumienie zdarzeń i obsługi zdarzeń w C#

Rozumiem cel zdarzeń, szczególnie w kontekście tworzenia interfejsów użytkownika. Myślę, że jest to prototyp do tworzenia zdarzenia:

public void EventName(object sender, EventArgs e);

Co robią procedury obsługi zdarzeń, dlaczego są one potrzebne i jak je utworzyć?

Author: Peter Mortensen, 2009-04-29

9 answers

Aby zrozumieć procedury obsługi zdarzeń, musisz zrozumiećdelegatów . W C# możesz myśleć o delegacie jako wskaźniku (lub odwołaniu) do metody. Jest to przydatne, ponieważ wskaźnik może być przekazywany jako wartość.

Centralnym pojęciem delegata jest jego podpis, czyli kształt. To jest (1) typ zwracany i (2) argumenty wejściowe. Na przykład, jeśli utworzymy delegat void MyDelegate(object sender, EventArgs e), może on wskazywać tylko na metody, które zwracają void i przyjmują object i EventArgs. Rodzaj jak kwadratowa dziura i kwadratowy kołek. Mówimy więc, że te metody mają ten sam podpis lub kształt, co delegat.

Wiedząc więc, jak stworzyć odniesienie do metody, zastanówmy się nad celem zdarzeń: chcemy spowodować wykonanie jakiegoś kodu, gdy coś się wydarzy w innym miejscu systemu - lub "obsłużyć Zdarzenie". W tym celu tworzymy konkretne metody dla kodu, który chcemy wykonać. Spoiwem pomiędzy wydarzeniem a metodami do wykonania są delegaci. Wydarzenie musi wewnętrznie przechowuje "listę" wskaźników do metod, które mają być wywołane po wywołaniu zdarzenia.* Oczywiście, aby móc wywołać metodę, musimy wiedzieć, jakie argumenty do niej przekazać! Używamy delegata jako" umowy " pomiędzy wydarzeniem a wszystkimi określonymi metodami, które zostaną wywołane.

Więc domyślna EventHandler (i wiele podobnych) reprezentuje specyficzny kształt metody (ponownie, void / object-EventArgs). Kiedy deklarujesz Zdarzenie, mówisz jaki kształt metody (EventHandler) to zdarzenie wywoła, podając delegata:

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

(*jest to klucz do wydarzeń W. NET i odrywa "magię" - wydarzenie jest tak naprawdę, pod przykrywką, tylko listą metod o tym samym "kształcie". Lista jest przechowywana tam, gdzie odbywa się wydarzenie. Kiedy zdarzenie jest "podniesione", tak naprawdę wystarczy"przejść przez tę listę metod i wywołać każdą z nich, używając tych wartości jako parametrów". Przypisanie obsługi zdarzenia jest po prostu ładniejszym i łatwiejszym sposobem dodania metody do tego lista metod do wywołania).

 555
Author: Rex M,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-31 12:38:51

C# zna dwa terminy, delegate i event. Zacznijmy od pierwszego.

Delegat

A delegate jest odniesieniem do metody. Podobnie jak można utworzyć odniesienie do instancji:

MyClass instance = myFactory.GetInstance();

Możesz użyć delegata do utworzenia odniesienia do metody:

Action myMethod = myFactory.GetInstance;

Teraz, gdy masz odniesienie do metody, możesz wywołać metodę poprzez odniesienie:

MyClass instance = myMethod();
Ale dlaczego? Możesz również zadzwonić bezpośrednio do myFactory.GetInstance(). W tym przypadku możesz. Jednakże, istnieje wiele przypadków do przemyślenia, w których nie chcesz, aby reszta aplikacji miała wiedzę o myFactory lub aby zadzwonić bezpośrednio myFactory.GetInstance().

Oczywistym jest, jeśli chcesz być w stanie zastąpić myFactory.GetInstance() na myOfflineFakeFactory.GetInstance() z jednego centralnego miejsca (aka wzór metody fabrycznej).

Wzór metody Fabrycznej

Więc jeśli masz klasę TheOtherClass I musi ona użyć myFactory.GetInstance(), tak będzie wyglądał kod bez delegatów (musisz poinformować TheOtherClass o rodzaj twojego myFactory):

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


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

}

Jeśli używasz delegatów, nie musisz ujawniać typu mojej fabryki:

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


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

}

W ten sposób możesz dać delegata innej klasie do użycia, bez wystawiania na nie swojego typu. Jedyną rzeczą, którą ujawniasz, jest podpis Twojej metody(ile masz parametrów i takich).

"Signature of my method", where did I hear that before? O tak, interfejsy!!! interfejsy opisują sygnaturę całej klasy. Pomyśl o delegatach jako opisując podpis tylko jednej metody!

Kolejną dużą różnicą między interfejsem a delegatem jest to, że pisząc klasę, nie musisz mówić do C# "ta metoda implementuje ten typ delegata". W przypadku interfejsów musisz powiedzieć "ta klasa implementuje ten typ interfejsu".

Ponadto odwołanie delegata może (z pewnymi ograniczeniami, patrz poniżej) odwoływać się do wielu metod (zwanych MulticastDelegate). Oznacza to, że po wywołaniu delegata, zostanie wykonanych wiele jawnie dołączonych metod. Odniesienie do obiektu może zawsze odnosić się tylko do jednego obiektu.

Ograniczenia dla MulticastDelegate są takie, że podpis (metoda/delegat) nie powinien mieć żadnej wartości zwrotnej (void), A Słowa kluczowe out i ref nie są używane w podpisie. Oczywiście nie można wywołać dwóch metod, które zwracają numer i oczekiwać, że zwrócą ten sam numer. Po zatwierdzeniu podpisu delegat jest automatycznie MulticastDelegate.

Zdarzenie

Zdarzenia są tylko właściwościami (jak pola get;set; properties to instance), które ujawniają subskrypcję delegata z innych obiektów. Te właściwości nie obsługują jednak get; set;. Zamiast tego wspierają dodawanie; usuwanie;

Więc możesz mieć:

    Action myField;

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

Użycie w UI (WinForms)

Więc teraz wiemy, że delegat jest odniesieniem do metody i że możemy mieć wydarzenie, aby dać światu znać, że mogą dać nam swoje metody do być odwołane z naszego delegata, i jesteśmy przycisk UI, następnie: możemy poprosić każdego, kto jest zainteresowany, czy zostałem kliknięty, aby zarejestrować swoją metodę z nami(poprzez zdarzenie, które narażone). Możemy użyć tych wszystkich metod, które zostały nam dane i odwoływać się do nich przez naszego delegata. A potem będziemy czekać i czekać.... dopóki użytkownik nie przyjdzie i nie kliknie tego przycisku, będziemy mieli wystarczający powód, aby wywołać delegata. A ponieważ delegat odwołuje się do wszystkich podanych nam metod, wszystkie te metody będą być powoływany. Nie wiemy, co robią te metody, ani nie wiemy, która klasa implementuje te metody. Zależy nam tylko na tym, aby ktoś był zainteresowany kliknięciem i podał nam odniesienie do metody zgodnej z naszym pożądanym podpisem.

Java

Języki takie jak Java nie mają delegatów. Zamiast tego używają interfejsów. Sposób, w jaki to robią, to poproszenie każdego, kto jest zainteresowany "kliknięciem nas", o zaimplementowanie pewnego interfejsu (z pewną metodą możemy call), następnie podaj nam całą instancję implementującą interfejs. Przechowujemy listę wszystkich obiektów implementujących ten interfejs i możemy wywołać ich "pewną metodę, którą możemy wywołać" za każdym razem, gdy zostaniemy kliknięci.

 90
Author: tofi9,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-30 02:06:24

Oto przykład kodu, który może pomóc:

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();
    }
  }
}
 36
Author: Gary Willoughby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-29 18:36:11

Jest to deklaracja dla obsługi zdarzenia - metody, która zostanie wywołana po wywołaniu zdarzenia. Aby utworzyć wydarzenie, napiszesz coś takiego:

public class Foo
{
    public event EventHandler MyEvent;
}

A następnie możesz zapisać się na wydarzenie w następujący sposób:

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

Z onmyevent () zdefiniowanym tak:

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

Gdy Foo odpali MyEvent, wtedy zostanie wywołany Twój opiekun OnMyEvent.

Nie zawsze musisz używać instancji EventArgs jako drugiego parametru. Jeśli chcesz zawierać dodatkowe informacje, można użyć klasy pochodzącej z EventArgs (EventArgs jest bazą według konwencji). Na przykład, jeśli przyjrzysz się niektórym zdarzeniom zdefiniowanym na Control W WinForms lub FrameworkElement w WPF, możesz zobaczyć przykłady zdarzeń, które przekazują dodatkowe informacje do procedur obsługi zdarzeń.

 31
Author: Andy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-29 16:46:57

Aby dodać do istniejących tutaj wielkich odpowiedzi-opierając się na kodzie w zaakceptowanym, który używa delegate void MyEventHandler(string foo)...

Ponieważ kompilator zna typ delegata zdarzenia SomethingHappened, to:

myObj.SomethingHappened += HandleSomethingHappened;

Jest całkowicie równoważne:

myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);

I obsługa może być również Niezarejestrowana z -= tak:

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

Ze względu na kompletność, podnoszenie zdarzenia może być wykonane w ten sposób, tylko w klasie, która posiada Zdarzenie:

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

Thread-local copy of the handler is needed to make sure the invocation is thread-safe - w przeciwnym razie thread mógłby przejść i wyrejestrować ostatni handler dla zdarzenia natychmiast po sprawdzeniu, czy to null, i mielibyśmy tam " fun " NullReferenceException.


[[9]}C# 6 wprowadził ładną krótką rękę dla tego wzoru. Używa operatora propagacji null.
SomethingHappened?.Invoke("Hi there!");
 20
Author: Mathieu Guindon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-29 00:30:01

Moje rozumienie wydarzeń jest;

Delegat:

Zmienna przechowująca odniesienie do metody / metod do wykonania. Umożliwia to przekazywanie metod jak zmiennej.

Kroki tworzenia i wywoływania zdarzenia:

  1. Zdarzenie jest instancją delegata

  2. Ponieważ zdarzenie jest instancją delegata, musimy najpierw zdefiniować delegata.

  3. Przypisz metodę / metody do wykonania po wywołaniu zdarzenia (wywołanie delegata)

  4. Odpal Zdarzenie (wywołanie delegata)

Przykład:

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
Author: KE50,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-06 07:09:17

Wydawca: where the events happen. Wydawca powinien określić, którego delegata używa klasa i wygenerować niezbędne argumenty, przekazać te argumenty i siebie do delegata.

Subscriber: where the response happen. Subskrybent powinien określić metody reagowania na zdarzenia. Metody te powinny przyjmować ten sam typ argumentów co delegat. Subskrybent następnie dodać tę metodę do delegata wydawcy.

Dlatego, gdy wydarzenie się wydarzy w publisher, delegat otrzyma niektóre argumenty zdarzenia (dane, itp.), ale wydawca nie ma pojęcia, co się stanie z tymi wszystkimi danymi. Subskrybenci mogą tworzyć metody w swojej klasie, aby odpowiadać na zdarzenia w klasie wydawcy, tak aby subskrybenci mogli odpowiadać na zdarzenia wydawcy.

 3
Author: rileyss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-30 00:47:45
//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
Author: Bilgi Sayar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-29 14:43:21

Zgadzam się z KE50 z tym wyjątkiem, że słowo kluczowe 'event' jest aliasem dla 'ActionCollection', ponieważ zdarzenie zawiera zbiór akcji do wykonania (np. delegat).

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
Author: user3902302,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-16 21:03:57