Понимание событий и обработчиков событий в C #

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

public void EventName(object sender, EventArgs e); 

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

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

Центральная концепция делегата – это его подпись или форма. То есть (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() из одного центрального места (так называемого шаблона фабричного метода ).

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

Итак, если у вас есть class 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 } } 

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

«Подпись моего метода», откуда я это раньше слышал? O да, интерфейсы !!! интерфейсы описывают подпись целого classа. Подумайте о том, что delegates описывают подпись только одного метода!

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

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

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

Мероприятие

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

Таким образом, вы можете:

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

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

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

Ява

Языки, подобные 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 ваш обработчик OnMyEvent .

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

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

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

 myObj.SomethingHappened += HandleSomethingHappened; 

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

 myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened); 

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

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

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

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

Локальная копия обработчика streamа необходима, чтобы удостовериться, что вызов является streamобезопасным – иначе stream может пойти и отменить регистрацию последнего обработчика события сразу после того, как мы проверили, было ли оно 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(); } } } } 

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

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

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

 //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 EventActions=new List(); // 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(); } } } } 
  • Выделить событие с текстовым полем?
  • Отслеживание положения прокрутки и уведомление об этом других компонентах
  • Событие Click не работает с динамически сгенерированными элементами
  • Как использовать GWT EventBus
  • Получить отредактированный TreeNode от CellEditorListener
  • Как я могу отменить регистрацию «анонимного» обработчика событий
  • Как я могу создать событие в usercontrol и поймать его на главной странице?
  • Что такое WPF Preview Events?
  • Реализация сканера штрих-кода на Java
  • Автоматически создавать пустые обработчики событий C #
  • Когда происходит событие «размытия», как я могу узнать, какой элемент фокуса пошел * на *?
  • Давайте будем гением компьютера.