События / делегаты в 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 события - это просто делегаты под капотом: они предоставляют некоторый синтаксический сахар в компиляторе.

вы можете установить/сбросить делегата, но вы можете только добавить или удалить обработчик событий. Причина в том, что вам все равно, кто еще подписывается на событие, тогда как простые делегаты больше используются в сценарии "обратного вызова".

но в конце концов они очень, очень похожи.

ресурсы:

событий С# и делегаты

делегаты и события-короткий Q & A


Я новичок в мире 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");
    }
}

Не стесняйтесь спрашивать, исправления, предложения =) С наилучшими пожеланиями!