События / делегаты в Java или C#
Я пытался узнать о событиях / делегатах, но запутался в отношениях между ними. Я знаю, что делегаты позволяют вызывать различные функции без необходимости знать, какая конкретная функция вызывается. (например: графическая функция должна принимать входные данные, которые являются различными функциями, которые должны быть графически).
но я не вижу, как делегаты используются в событиях.
может ли кто-нибудь построить простой пример (в псевдокоде или C# или Java), который иллюстрирует работу делегатов как связанную с событиями?
спасибо!
6 ответов
(все это с точки зрения C#.)
у меня есть статья о различиях между мероприятиями и делегатами. Это охватывает все, что упоминается ниже, более подробно.
в основном мне нравится думать о событии как о свойстве-это пара методов, вот и все. Вместо get/set событие имеет add/remove - значение "добавить этот обработчик событий" и "удалить этот обработчик событий". По сути, это все событие есть.
C# также имеет полевые события, которые являются ярлык:
public event EventHandler Foo;
объявляет оба поля и событие с почти тривиальной реализацией добавления/удаления. Внутри класса, ссылаясь на Foo
относится к полю. Вне класса, ссылаясь на Foo
относится к событию.
основная идея заключается в том, что событие позволяет другому коду подписаться и отказаться от него, передав делегат (событие проводник). Обычно подписка реализуется путем создания нового многоадресного делегата, содержащего список обработчиков событий и новой. Поэтому, если вы храните обработчики событий в поле с именем myEventHandlers
, осуществление подписки может быть:
myEventHandlers += value;
аналогично отписка обычно включает в себя создание нового делегата многоадресной рассылки без указанного обработчика:
myEventHandlers -= value;
тогда, когда вы хотите поднимите / запустите событие, вы просто вызываете этот делегат многоадресной рассылки-обычно с проверкой nullity, чтобы избежать исключения, если никто не подписался:
EventHandler handler = myEventHandlers;
if (handler != null)
{
// You could pass in a different "sender" and "args" of course
handler(this, EventArgs.Empty);
}
используя события, подписчики не знают друг о друге и не могут сами поднять событие (обычно). Другими словами, это шаблон инкапсуляции, который получил статус как в языке, так и в платформе.
вам нужно будет уточнить, какой язык вы хотите. Насколько я знаю, Java не имеет понятия делегатов (хотя я мог бы быть совершенно неправ); он имеет тенденцию следовать шаблону наблюдателя для обработки событий.
C#, однако, не делает. Ан event
в C# имеет такое же отношение к делегату, как свойство к его резервному полю. Сам делегат-это то, что хранит указатель на функцию, которая обрабатывает событие (или, точнее, список указателей, добавленные в событие; я использую термин "указатель" свободно здесь).
если я объявлю это в C#:
public event EventHandler MyEvent;
и вызовите событие следующим образом:
MyEvent(this, EventArgs.Empty);
это действительно просто стенография для полной реализации события:
private EventHandler myEventHandler;
public event EventHandler MyEvent
{
add { myEventHandler += value; }
remove { myEventHandler -= value; }
}
и вызываю его...
myEventHandler(this, EventArgs.Empty);
все это означает, что фактический event
предоставляет две операции: add
и remove
которые используются потребляющим кодом для присоединения обработчиков событий к событию. В по умолчанию (сокращенно), компилятор создает частный экземпляр члена типа делегата и использует его так, как я описал выше. Когда вы" вызываете " событие, компилятор фактически заменяет имя события именем созданного им частного делегата поддержки. Вот почему вы не можете вызвать event
из подкласса--если событие создано в стенографии, то вспомогательным элементом является private
.
разница проста.
delegate
- класс с двумя полями-object и MethodInfo.
event
является частным полем типа delegate
и два открытых метода add
и remove
.
обычно под капотом событие MulticastDelegate
используется-это класс, унаследованный от Delegate
и содержащий список делегатов. Это позволяет событию иметь несколько подписчиков.
вы можете посмотреть на: http://msdn.microsoft.com/en-us/library/17sde2xt.aspx
пример продолжается здесь: http://msdn.microsoft.com/en-us/library/xwbwks95.aspx
в основном, как уже упоминалось, события - это просто особые случаи делегатов, но с изменениями в .NET 3.5 вы можете писать события без использования делегатов, хотя под капотом делегаты все еще записываются.
Если вы посмотрите на эту статью, они показывают, как использование лямбда-выражений и анонимных функций для событий: http://msdn.microsoft.com/en-us/library/ms366768.aspx
.Net события - это просто делегаты под капотом: они предоставляют некоторый синтаксический сахар в компиляторе.
вы можете установить/сбросить делегата, но вы можете только добавить или удалить обработчик событий. Причина в том, что вам все равно, кто еще подписывается на событие, тогда как простые делегаты больше используются в сценарии "обратного вызова".
но в конце концов они очень, очень похожи.
ресурсы:
Я новичок в мире java, но я должен признать, что я очень рад, но я все еще скучаю по некоторым вещам C#, поэтому создайте этот шаблон, который дал мне хорошие результаты, эксперты Java видят некоторый недостаток в использовании этого шаблона? Он поддерживает только java 8:
@FunctionalInterface
public interface IEvent<TEventArgs extends Object> {
void invoke(TEventArgs eventArgs);
}
public class EventHandler<TEventArgs>
{
private ArrayList<IEvent<TEventArgs>> eventDelegateArray = new ArrayList<>();
public void subscribe(IEvent<TEventArgs> methodReference)
{
eventDelegateArray.add(methodReference);
}
public void unSubscribe(IEvent<TEventArgs> methodReference)
{
eventDelegateArray.remove(methodReference);
}
public void invoke(TEventArgs eventArgs)
{
if (eventDelegateArray.size()>0)
eventDelegateArray.forEach(p -> p.invoke(eventArgs));
}
}
public class DummyEventProducer
{
// The event
public EventHandler<String> myEvent = new EventHandler<>();
public void onMyEvent(String A)
{
myEvent.invoke(A);
}
}
public class DummySubscriber {
// The method will be subscribed to the event
public void methodCallWhenEventGetTriggered(String eventArgs)
{
System.out.println("event fired with eventargs: " + eventArgs);
}
}
public class Main {
public static void main(String[] args)
{
// A dummy producer
DummyEventProducer producer = new DummyEventProducer();
// A dummy subscribers
DummySubscriber testingInstanceA = new DummySubscriber();
DummySubscriber testingInstanceB = new DummySubscriber();
DummySubscriber testingInstanceC = new DummySubscriber();
// We create decoupled event links because we want to un-subscribe later
IEvent<String> EventSink1 = testingInstanceA::methodCallWhenEventGetTriggered;
IEvent<String> EventSink2 = testingInstanceB::methodCallWhenEventGetTriggered;
IEvent<String> EventSink3 = testingInstanceC::methodCallWhenEventGetTriggered;
// subscribe to the event on dummy producer
producer.myEvent.subscribe(EventSink1);
producer.myEvent.subscribe(EventSink2);
producer.myEvent.subscribe(EventSink3);
// fire the event on producer
producer.onMyEvent("Hola MUNDO with decoupled subscriptions!");
// unsubscribe to the event on dummy producer
producer.myEvent.unSubscribe(EventSink1);
producer.myEvent.unSubscribe(EventSink2);
producer.myEvent.unSubscribe(EventSink3);
// fire the event on producer again
producer.onMyEvent("Hola MUNDO! with no events subscriptions :(");
// IF YOU DON CARE ABOUT UNSUBSCRIBE YOU CAN LINK EVENTS DIRECTLY TO THE SUBSCRIBER
producer.myEvent.subscribe(testingInstanceA::methodCallWhenEventGetTriggered);
producer.myEvent.subscribe(testingInstanceB::methodCallWhenEventGetTriggered);
producer.myEvent.subscribe(testingInstanceC::methodCallWhenEventGetTriggered);
// fire the event on producer again
producer.onMyEvent("Hola MUNDO! with strong link subscriptions (cannot be un-subscribed");
}
}
Не стесняйтесь спрашивать, исправления, предложения =) С наилучшими пожеланиями!