Я понимаю цель событий, особенно в контексте создания пользовательских интерфейсов. Я думаю, что это прототип для создания события:
public void EventName(object sender, EventArgs e);
Что делают обработчики событий, зачем они нужны и как их создать?
c#
.net
events
event-handling
Леви Кэмпбелл
источник
источник
Ответы:
Чтобы понимать обработчики событий, вам нужно понимать делегатов . В C # вы можете рассматривать делегата как указатель (или ссылку) на метод. Это полезно, потому что указатель может быть передан как значение.
Центральным понятием делегата является его подпись или форма. Это (1) возвращаемый тип и (2) входные аргументы. Например, если мы создаем делегат
void MyDelegate(object sender, EventArgs e)
, он может указывать только на методы, которые возвращаютvoid
, и принимаютobject
иEventArgs
. Вроде как квадратное отверстие и квадратный колышек. Поэтому мы говорим, что эти методы имеют ту же сигнатуру или форму, что и делегат.Итак, зная, как создать ссылку на метод, давайте подумаем о цели событий: мы хотим, чтобы какой-то код выполнялся, когда что-то происходит в другом месте системы - или «обрабатывал событие». Для этого мы создаем специальные методы для кода, который мы хотим выполнить. Склейка между событием и выполняемыми методами - это делегаты. Событие должно внутренне хранить «список» указателей на методы, вызываемые при возникновении события. * Конечно, чтобы иметь возможность вызывать метод, нам нужно знать, какие аргументы передать ему! Мы используем делегата в качестве «контракта» между событием и всеми конкретными методами, которые будут вызваны.
Таким образом, значение по умолчанию
EventHandler
(и многим оно нравится) представляет конкретную форму метода (опять же, void / object-EventArgs). Когда вы объявляете событие, вы говорите, какую форму метода (EventHandler) это событие будет вызывать, указав делегата:(* Это ключ к событиям в .NET и очищает от "магии" - событие действительно, под прикрытием, просто список методов той же "формы". Список хранится там, где оно происходит. Когда событие «возбуждено», это действительно просто «пройти через этот список методов и вызвать каждый из них, используя эти значения в качестве параметров». Назначение обработчика событий - это просто более красивый и простой способ добавления вашего метода в этот список методов быть позванным).
источник
C # знает два термина,
delegate
иevent
. Давайте начнем с первого.делегат
A
delegate
является ссылкой на метод. Также как вы можете создать ссылку на экземпляр:Вы можете использовать делегата для создания ссылки на метод:
Теперь, когда у вас есть эта ссылка на метод, вы можете вызвать метод через ссылку:
Но почему ты? Вы также можете просто позвонить
myFactory.GetInstance()
напрямую. В этом случае вы можете. Однако, есть много случаев, чтобы подумать о том, где вы не хотите, чтобы остальная часть приложения зналаmyFactory
или вызывалаmyFactory.GetInstance()
напрямую.Очевидным является, если вы хотите иметь возможность заменить
myFactory.GetInstance()
вmyOfflineFakeFactory.GetInstance()
одном центральном месте (он же шаблон фабричного метода ).Шаблон фабричного метода
Итак, если у вас есть
TheOtherClass
класс, и он должен использоватьmyFactory.GetInstance()
, вот как будет выглядеть код без делегатов (вам нужно сообщитьTheOtherClass
о типе вашегоmyFactory
):Если вы используете делегатов, вам не нужно указывать тип моей фабрики:
Таким образом, вы можете передать делегат другому классу для использования, не раскрывая им ваш тип. Единственное, что вы выставляете - это подпись вашего метода (сколько у вас параметров и тому подобное).
«Подпись моего метода», где я слышал это раньше? О да, интерфейсы !!! интерфейсы описывают сигнатуру целого класса. Представьте, что делегаты описывают сигнатуру только одного метода!
Еще одно большое различие между интерфейсом и делегатом заключается в том, что при написании класса вам не нужно говорить на C # «этот метод реализует этот тип делегата». Что касается интерфейсов, вам нужно сказать «этот класс реализует этот тип интерфейса».
Кроме того, ссылка на делегат может (с некоторыми ограничениями, см. Ниже) ссылаться на несколько методов (вызванных
MulticastDelegate
). Это означает, что при вызове делегата будет выполнено несколько явно присоединенных методов. Ссылка на объект всегда может ссылаться только на один объект.Ограничения для a
MulticastDelegate
заключаются в том, что подпись (метод / делегат) не должна иметь никакого возвращаемого значения (void
) и ключевых словout
иref
не используется в подписи. Очевидно, что вы не можете вызвать два метода, которые возвращают число и ожидают, что они вернут один и тот же номер. Как только подпись соответствует, делегат автоматически становитсяMulticastDelegate
.Мероприятие
События - это просто свойства (например, get; set; свойства для полей экземпляра), которые предоставляют подписку делегату от других объектов. Эти свойства, однако, не поддерживают get; set ;. Вместо этого они поддерживают add; удалять;
Таким образом, вы можете иметь:
Использование в пользовательском интерфейсе (WinForms, WPF, UWP и т. Д.)
Итак, теперь мы знаем, что делегат является ссылкой на метод и что у нас может быть событие, которое позволит миру узнать, что они могут дать нам свои методы, на которые будут ссылаться наши делегаты, и мы являемся кнопкой UI, тогда: мы может попросить любого, кто интересуется, нажали ли на меня, зарегистрировать свой метод у нас (через событие, которое мы выставили). Мы можем использовать все те методы, которые были нам даны, и ссылаться на них нашим делегатом. А потом мы будем ждать и ждать .... пока пользователь не придет и не нажмет на эту кнопку, у нас будет достаточно причин для вызова делегата. И поскольку делегат ссылается на все эти методы, данные нам, все эти методы будут вызваны. Мы не знаем, что делают эти методы, и мы не знаем, какой класс реализует эти методы. Все, что нас волнует, это то, что кто-то был заинтересован в том, чтобы нас нажимали,
Ява
Такие языки, как Java, не имеют делегатов. Вместо этого они используют интерфейсы. Они делают так, чтобы попросить любого, кто заинтересован в том, чтобы «нас нажимали», реализовать определенный интерфейс (с помощью определенного метода, который мы можем вызвать), а затем предоставить нам весь экземпляр, который реализует интерфейс. Мы храним список всех объектов, реализующих этот интерфейс, и можем вызывать их «определенный метод, который мы можем вызвать» всякий раз, когда нас нажимают.
источник
event
это всего лишь синтаксис, больше ничего.Это фактически объявление для обработчика событий - метода, который будет вызываться при запуске события. Чтобы создать событие, вы должны написать что-то вроде этого:
И тогда вы можете подписаться на событие, как это:
С OnMyEvent () определяется следующим образом:
Всякий раз, когда
Foo
срабатываетMyEvent
, вашOnMyEvent
обработчик будет вызван.Вам не всегда нужно использовать экземпляр
EventArgs
как второй параметр. Если вы хотите включить дополнительную информацию, вы можете использовать класс, полученный изEventArgs
(EventArgs
является основой по соглашению). Например, если вы посмотрите на некоторые события, определенныеControl
в WinForms илиFrameworkElement
в WPF, вы увидите примеры событий, которые передают дополнительную информацию обработчикам событий.источник
OnXXX
шаблон именования для вашего обработчика событий. (Глупо, OnXXX означает «обрабатывать XXX» в MFC и «поднимать XXX» в .net, и теперь его значение неясно и запутанно - подробности см. В этом посте ). Предпочтительные имена будутRaiseXXX
вызывать события иHandleXXX
илиSender_XXX
для обработчиков событий.Вот пример кода, который может помочь:
источник
OnMaximum?.Invoke(this,new MyEventArgs("you've entered..."));
Просто чтобы добавить к существующим отличным ответам здесь - на основе кода в принятом, который использует
delegate void MyEventHandler(string foo)
...Поскольку компилятор знает тип делегата события SomethingHappened , это:
Полностью эквивалентно:
И обработчики также могут быть незарегистрированы с помощью
-=
этого:Ради полноты, поднятие события может быть сделано следующим образом, только в классе, которому принадлежит событие:
Нить-локальная копия обработчика необходимо , чтобы убедиться , что вызов является поточно-- в противном случае поток может идти и незарегистрированный последний обработчик события сразу же после того, как мы проверили , если это было
null
, и мы бы иметь «удовольствие»NullReferenceException
там ,C # 6 представил хорошую короткую руку для этого паттерна. Он использует нулевой оператор распространения.
источник
Мое понимание событий таково;
Делегирование:
Переменная для хранения ссылки на метод / методы, которые будут выполнены. Это позволяет передавать методы как переменную.
Шаги для создания и вызова события:
Событие является экземпляром делегата
Поскольку событие является экземпляром делегата, мы должны сначала определить делегат.
Назначьте метод / методы, которые будут выполняться при возникновении события ( вызов делегата )
Запустить событие ( Позвонить делегату )
Пример:
источник
Издатель: где происходят события. Издатель должен указать, какой делегат используется классом, и сгенерировать необходимые аргументы, передать эти аргументы и самому делегату.
подписчик: где происходит ответ. Подписчик должен указать методы реагирования на события. Эти методы должны принимать аргументы того же типа, что и делегат. Затем подписчик добавляет этот метод к делегату издателя.
Поэтому, когда событие происходит в издателе, делегат получит некоторые аргументы события (данные и т. Д.), Но издатель не знает, что произойдет со всеми этими данными. Подписчики могут создавать методы в своем собственном классе, чтобы отвечать на события в классе издателя, чтобы подписчики могли отвечать на события издателя.
источник
источник
Я согласен с KE50, за исключением того, что я рассматриваю ключевое слово «событие» как псевдоним для «ActionCollection», так как событие содержит коллекцию действий, которые должны быть выполнены (т. Е. Делегат).
источник
Отличные технические ответы в посте! Технически мне нечего добавить к этому.
Одной из основных причин появления новых функций в языках и программном обеспечении в целом является маркетинг или политика компании! :-) Это не должно быть недооценено!
Я думаю, что это относится в определенной степени к делегатам и событиям! я нахожу их полезными и добавляю ценность языку C #, но с другой стороны язык Java решил не использовать их! они решили, что все, что вы решаете с делегатами, вы уже можете решить с помощью существующих функций языка, например, интерфейсов, например
В 2001 году Microsoft выпустила платформу .NET и язык C # как конкурентное решение для Java, поэтому было хорошо иметь НОВЫЕ ФУНКЦИИ, которых нет в Java.
источник
Недавно я сделал пример использования событий в c # и разместил его в своем блоге. Я попытался сделать это как можно более понятным, с очень простым примером. В случае, если это может кому-нибудь помочь, вот оно: http://www.konsfik.com/using-events-in-csharp/
Он включает в себя описание и исходный код (с большим количеством комментариев) и в основном фокусируется на правильном (подобном шаблону) использовании событий и обработчиков событий.
Некоторые ключевые моменты:
События похожи на «подтипы делегатов», только более ограниченные (в хорошем смысле). Фактически объявление события всегда включает делегата (EventHandlers - это тип делегата).
Обработчики событий - это особые типы делегатов (вы можете думать о них как о шаблоне), которые заставляют пользователя создавать события с определенной «подписью». Подпись имеет формат: (отправитель объекта, EventArgs Eventarguments).
Вы можете создать свой собственный подкласс EventArgs, чтобы включить любой тип информации, которую должно передать событие. Нет необходимости использовать EventHandlers при использовании событий. Вы можете полностью пропустить их и использовать вместо них своего представителя.
Одно ключевое отличие между использованием событий и делегатов заключается в том, что события могут вызываться только из класса, в котором они были объявлены, даже если они могут быть объявлены как открытые. Это очень важное различие, потому что оно позволяет выставлять ваши события так, чтобы они были «связаны» с внешними методами, и в то же время они были защищены от «внешнего злоупотребления».
источник
Еще одна вещь, о которой нужно знать , в некоторых случаях вы должны использовать Делегаты / События, когда вам нужен низкий уровень связи !
Если вы хотите использовать компонент в нескольких местах приложения , вам нужно создать компонент с низким уровнем связи, и конкретная неосмотрительная ЛОГИКА должна быть делегирована ВНЕ вашего компонента! Это гарантирует, что у вас есть отделенная система и более чистый код.
В ТВЕРДОЙ принципе это « D », ( D ependency принцип инверсии).
Также известный как « IoC », инверсия управления .
Вы можете сделать « IoC » с событиями, делегатами и DI (Dependency Injection).
Легко получить доступ к методу в дочернем классе. Но сложнее получить доступ к методу в родительском классе от child. Вы должны передать родительскую ссылку ребенку! (или используйте DI с интерфейсом)
Делегаты / События позволяет нам общаться от ребенка к родителю без ссылки!
На этой диаграмме выше я не использую Delegate / Event, и родительский компонент B должен иметь ссылку на родительский компонент A, чтобы выполнить безразличную бизнес-логику в методе A. (высокий уровень связи)
При таком подходе мне пришлось бы поместить все ссылки на все компоненты, которые используют компонент B! :(
На этой диаграмме выше я использую Delegate / Event, и компонент B не должен знать A. (низкий уровень связи)
И вы можете использовать свой компонент B в любом месте вашего приложения !
источник