Вопрос

Я понимаю цель мероприятий, особенно в контексте создания пользовательских интерфейсов.Я думаю, что это прототип для создания события:

public void EventName(object sender, EventArgs e);

Что делают обработчики событий, зачем они нужны и как их создать?

Это было полезно?

Решение

Чтобы понять обработчики событий, вам необходимо понять делегатыС#, вы можете думать о делегате как об указателе (или ссылке) на метод.Это полезно, поскольку указатель можно передавать как значение.

Центральным понятием делегата является его подпись или форма.Это (1) тип возвращаемого значения и (2) входные аргументы.Например, если мы создадим делегата void MyDelegate(object sender, EventArgs e), он может указывать только на методы, которые возвращают void, и возьмите object и EventArgs.Что-то вроде квадратного отверстия и квадратного колышка.Поэтому мы говорим, что эти методы имеют ту же сигнатуру или форму, что и делегат.

Итак, зная, как создать ссылку на метод, давайте подумаем о назначении событий:мы хотим, чтобы некоторый код выполнялся, когда что-то происходит где-то в системе, или «обрабатываем событие».Для этого мы создаем специальные методы для кода, который хотим выполнить.Связующим звеном между событием и методами, которые должны быть выполнены, являются делегаты.Событие должно хранить внутри себя «список» указателей на методы, которые следует вызывать при возникновении события.* Конечно, чтобы иметь возможность вызвать метод, нам нужно знать, какие аргументы ему передать!Мы используем делегат как «контракт» между событием и всеми конкретными методами, которые будут вызываться.

Итак, по умолчанию EventHandler (и многим это нравится) представляет собой конкретная форма метода (опять же, void/object-EventArgs).Когда вы объявляете событие, вы говорите какая форма метода (EventHandler), это событие будет вызываться путем указания делегата:

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

(*Это ключ к событиям в .NET, который устраняет «магию» - событие на самом деле, под обложкой, просто список методов одной и той же «формы».Список хранится там, где происходит событие.Когда событие «вызывается», на самом деле оно просто «проходит по этому списку методов и вызывает каждый из них, используя эти значения в качестве параметров».Назначение обработчика событий — это просто более красивый и простой способ добавить ваш метод в список вызываемых методов).

Другие советы

C# знает два термина, delegate и event.Начнем с первого.

Делегат

А delegate является ссылкой на метод.Точно так же, как вы можете создать ссылку на экземпляр:

MyClass instance = myFactory.GetInstance();

Вы можете использовать делегат для создания ссылки на метод:

Action myMethod = myFactory.GetInstance;

Теперь, когда у вас есть ссылка на метод, вы можете вызвать метод через ссылку:

MyClass instance = myMethod();

Но зачем вам это?Вы также можете просто позвонить myFactory.GetInstance() напрямую.В этом случае вы можете.Однако есть много случаев, о которых следует подумать, когда вы не хотите, чтобы остальная часть приложения знала о myFactory или позвонить myFactory.GetInstance() напрямую.

Очевидным является то, что если вы хотите иметь возможность заменить myFactory.GetInstance() в myOfflineFakeFactory.GetInstance() из одного центрального места (он же шаблон фабричного метода).

Шаблон фабричного метода

Итак, если у вас есть TheOtherClass класс, и он должен использовать myFactory.GetInstance(), так будет выглядеть код без делегатов (вам нужно будет разрешить TheOtherClass знать о типе своего myFactory):

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


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

}

Если вы будете использовать делегатов, вам не нужно раскрывать тип моей фабрики:

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


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

}

Таким образом, вы можете передать делегат другому классу для использования, не раскрывая им свой тип.Единственное, что вы раскрываете, — это сигнатуру вашего метода (сколько у вас параметров и тому подобное).

«Сигнатура моего метода», где я это слышал раньше?О да, интерфейсы!!!интерфейсы описывают сигнатуру целого класса.Думайте о делегатах как о описании сигнатуры только одного метода!

Еще одно большое различие между интерфейсом и делегатом заключается в том, что когда вы пишете свой класс, вам не нужно говорить C#: «Этот метод реализует этот тип делегата».Что касается интерфейсов, вам нужно сказать: «Этот класс реализует этот тип интерфейса».

Кроме того, ссылка на делегата может (с некоторыми ограничениями, см. ниже) ссылаться на несколько методов (называемых MulticastDelegate).Это означает, что при вызове делегата будет выполнено несколько явно присоединенных методов.Ссылка на объект всегда может ссылаться только на один объект.

Ограничения на MulticastDelegate заключается в том, что подпись (метод/делегат) не должна иметь никакого возвращаемого значения (void) и ключевые слова out и ref в подписи не используется.Очевидно, что вы не можете вызвать два метода, возвращающих число, и ожидать, что они вернут одно и то же число.Как только подпись соответствует требованиям, делегат автоматически становится MulticastDelegate.

Событие

События — это просто свойства (например, get;set;свойства для полей экземпляра), которые предоставляют подписку на делегата от других объектов.Однако эти свойства не поддерживают get;set;.Вместо этого они поддерживают добавление;удалять;

Итак, вы можете иметь:

    Action myField;

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

Использование в пользовательском интерфейсе (WinForms, WPF, UWP и т. д.)

Итак, теперь мы знаем, что делегат — это ссылка на метод и что у нас может быть событие, сообщающее миру, что они могут предоставить нам свои методы, на которые можно ссылаться из нашего делегата, и что мы — кнопка пользовательского интерфейса, тогда:мы можем попросить любого, кто интересуется, нажали ли на меня, зарегистрировать у нас свой метод (через событие, которое мы предоставили).Мы можем использовать все те методы, которые были нам предоставлены, и ссылаться на них нашим делегатом.А дальше будем ждать и ждать....пока пользователь не придет и не нажмет эту кнопку, у нас будет достаточно оснований для вызова делегата.А поскольку делегат ссылается на все данные нам методы, все эти методы будут вызваны.Мы не знаем, что делают эти методы, и не знаем, какой класс реализует эти методы.Все, что нас волнует, это то, что кто-то был заинтересован в том, чтобы на нас кликнули, и дал нам ссылку на метод, соответствующий нашей желаемой сигнатуре.

Джава

В таких языках, как Java, нет делегатов.Вместо этого они используют интерфейсы.Они делают это так: просят любого, кто заинтересован в том, чтобы «на нас нажимали», реализовать определенный интерфейс (с определенным методом, который мы можем вызвать), а затем предоставить нам весь экземпляр, реализующий этот интерфейс.Мы храним список всех объектов, реализующих этот интерфейс, и можем вызывать их «определенный метод, который мы можем вызвать» всякий раз, когда на нас нажимают.

Вот пример кода, который может помочь:

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

На самом деле это объявление обработчика событий — метода, который будет вызываться при возникновении события.Чтобы создать событие, вы должны написать что-то вроде этого:

public class Foo
{
    public event EventHandler MyEvent;
}

И тогда вы можете подписаться на событие следующим образом:

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

С OnMyEvent(), определенным следующим образом:

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

В любое время Foo выстреливает MyEvent, тогда твой OnMyEvent будет вызван обработчик.

Вам не всегда нужно использовать экземпляр EventArgs в качестве второго параметра.Если вы хотите включить дополнительную информацию, вы можете использовать класс, производный от EventArgs (EventArgs является базой по соглашению).Например, если вы посмотрите на некоторые события, определенные в Control в WinForms или FrameworkElement в WPF вы можете увидеть примеры событий, которые передают дополнительную информацию обработчикам событий.

Просто чтобы добавить к существующим здесь замечательным ответам - основываясь на принятом коде, который использует delegate void MyEventHandler(string foo)...

Поскольку компилятор знает тип делегата Что-то произошло событие, это:

myObj.SomethingHappened += HandleSomethingHappened;

Полностью эквивалентно:

myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);

И обработчики также могут быть незарегистрированный с -= так:

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

Для полноты картины вызов события можно выполнить следующим образом, только в классе, владеющем событием:

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

Локальная для потока копия обработчика необходима, чтобы убедиться, что вызов является потокобезопасным — в противном случае поток может пойти и отменить регистрацию последнего обработчика для события сразу после того, как мы проверим, было ли оно null, и нам бы "развлечься" NullReferenceException там.


В C# 6 появилось хорошее сокращенное обозначение этого шаблона.Он использует оператор распространения нуля.

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

Мое понимание событий таково;

Делегат:

Переменная для хранения ссылки на метод/методы, которые будут выполнены.Это позволяет передавать методы как переменную.

Шаги по созданию и вызову события:

  1. Событие является экземпляром делегата

  2. Поскольку событие является экземпляром делегата, нам необходимо сначала определить делегат.

  3. Назначьте метод/методы, которые будут выполняться при возникновении события (Вызов делегата)

  4. Запустить событие (Позвонить делегату)

Пример:

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

    }   

}

издатель:где происходят события.Издатель должен указать, какой делегат использует класс, и сгенерировать необходимые аргументы, передать эти аргументы и самого себя делегату.

подписчик:где происходит ответ.Подписчик должен указать методы реагирования на события.Эти методы должны принимать аргументы того же типа, что и делегат.Затем подписчик добавляет этот метод в делегат издателя.

Поэтому, когда событие происходит в издателе, делегат получит некоторые аргументы события (данные и т. д.), но издатель понятия не имеет, что произойдет со всеми этими данными.Подписчики могут создавать методы в своем собственном классе для реагирования на события в классе издателя, чтобы подписчики могли реагировать на события издателя.

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

Я согласен с KE50, за исключением того, что я рассматриваю ключевое слово «event» как псевдоним для «ActionCollection», поскольку событие содержит набор действий, которые необходимо выполнить (т.делегат).

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

}   

}

Отличные технические ответы в посте!у меня ничего нет технически добавить к этому.

Одной из основных причин появления новых функций в языках и программном обеспечении в целом является маркетинг или политика компании!:-) Это нельзя недооценивать!

Я думаю, что это в некоторой степени относится и к делегатам, и к мероприятиям!я нахожу их полезными и добавляю ценность языку C#, но, с другой стороны, язык Java решил их не использовать!они решили, что все, что вы решаете с помощью делегатов, вы уже можете решить с помощью существующих функций языка, т.е.интерфейсы, например

Примерно в 2001 году Microsoft выпустила .NET Framework и язык C# в качестве конкурента Java, поэтому было хорошо иметь НОВЫЕ ФУНКЦИИ, которых нет в Java.

Недавно я привел пример использования событий в C# и опубликовал его в своем блоге.Я постарался сделать это максимально понятно на очень простом примере.Если это может кому-то помочь, вот оно: http://www.konsfik.com/using-events-in-csharp/

Он включает описание и исходный код (с большим количеством комментариев) и в основном фокусируется на правильном (подобном шаблону) использовании событий и обработчиков событий.

Некоторые ключевые моменты:

  • События похожи на «подтипы делегатов», только более ограниченные (в хорошем смысле).Фактически объявление события всегда включает делегат (EventHandlers — это тип делегата).

  • Обработчики событий — это особые типы делегатов (вы можете думать о них как о шаблоне), которые заставляют пользователя создавать события, имеющие определенную «подпись».Подпись имеет формат:(отправитель объекта, аргументы событий EventArgs).

  • Вы можете создать свой собственный подкласс EventArgs, чтобы включить любой тип информации, которую необходимо передать событию.При использовании событий нет необходимости использовать EventHandlers.Вы можете полностью пропустить их и использовать вместо них свой собственный тип делегата.

  • Одно из ключевых различий между использованием событий и делегатов заключается в том, что события можно вызывать только из класса, в котором они были объявлены, даже если они могут быть объявлены как общедоступные.Это очень важное различие, поскольку оно позволяет раскрыть ваши события так, чтобы они были «связаны» с внешними методами, и в то же время они защищены от «внешнего неправильного использования».

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top