2016-03-08 3 views
2

Какова цель такой подписи в Observable.FromEvent? Для example:Подпись под наблюдением.FromEvent

var appActivated = Observable.FromEvent(
h => Application.Current.Activated += h, 
h => Application.Current.Activated -= h); 

В частности, что h? И почему +=, затем -=? Делаем ли мы Observable от события или от обработчика событий? Если от события, почему бы не просто подпись, как:

var appActivated = Observable.FromEvent(Application.Current.Activated); 
+1

Я написал длинное объяснение и обсуждение FromEvent некоторое время назад, которое могло бы быть полезным: http://stackoverflow.com/questions/19895373/how-to-use-observable-fromevent-instead-of-fromeventpattern-and -avoid-string-lit/19896246 # 19896246 –

+1

@James World: У меня было 60-70% понимания после чтения ответов здесь. Это> 95% после прочтения вашего сообщения. Особенно ваш абзац начинается с «Обратите внимание, что это только тот акт вызова Subscribe, который создает подписку». Мне очень помогли. – Bad

+0

Спасибо за отзыв. Это удивительно глубокий метод и требует много объяснений! –

ответ

3

Ответ Эрен правильный; Я хочу, чтобы убедиться, что все ваши вопросы отвечают:

In particular, what is h?

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

And why +=, then -=?

Наблюдаемый требует возможности как для подписчиков, так и для отмены подписки на событие.

Do we make Observable from event or from event handler?

От случая.

If from event, why not just have a signature like: var appActivated = Observable.FromEvent(Application.Current.Activated); ?

Потому что это передаст обработчик, а не событие. «Событие» - это три вещи: возможность вызова списка обработчиков, возможность добавления нового обработчика в список и возможность удаления обработчика из списка. Наблюдаемые нуждаются в последних двух; ваше предложение состоит в том, чтобы передать первое. Таким образом, наблюдаемый принимает делегатов, которые делают последние два.

+1

И для вашего последнего пункта он [предположительно] не пишет этот код из класса, определяющего это событие, поэтому он недействителен даже для доступа к списку обработчиков, нет способа написать метод «FromEvent», для которого этот код мог бы скомпилировать, не говоря уже о том, что нужно делать. – Servy

+0

То, что я на самом деле до сих пор не понимаю, следующее: у нас есть события, и мы хотим сделать из них поток событий. Поэтому здесь нет упоминания о каком-либо обработчике. И этот обработчик 'h' как бы появляется и исчезает из ниоткуда. Извините, может быть, я не понимаю что-то концептуально. – Bad

+1

@ Bad: Возможно, вы не понимаете, что такое лямбда? Когда вы говорите 'customers.Select (c => c.FirstName)' '' c' просто появляется «из ниоткуда». Вы понимаете, что лямбда - это всего лишь короткий способ написать метод? –

5

Это потому, что нет никакого способа, чтобы пройти в случае в качестве параметра к методу. Вы можете принять участие в мероприятии в качестве делегата , но это не дает вам возможности подписаться/отказаться от подписки на мероприятие. См. Это answer Эрика Липперта.

Observable.From в основном говорит: «Хорошо, я дам вам наблюдаемое, которое является оберткой вокруг события, но вам нужно предоставить мне двух делегатов: 1) делегат для меня, чтобы подписаться на моего обработчика события, и 2) делегат для меня, чтобы отказаться от подписки моего обработчика, когда мне нужно ».

Таким образом, в этом случае h => Application.Current.Activated += h является выражением лямбда, которое скомпилируется в делегат. h (обработчик) - это входной параметр, и делегат принимает этот входной параметр и подпишет его к событию Activated. И второй делегат - это одно и то же, за исключением того, что он отписывает обработчик.

2

Наблюдения - это первоклассные типы в .NET. Это означает, что вы можете хранить ссылки на них и передавать их в качестве параметров любому конструктору/методу, который вам нравится.

События не первоклассные типы. Они могут быть прикреплены и отсоединены только в том объеме, в котором вы можете ссылаться на свой содержащий объект.

Таким образом, это означает, что я не может это сделать:

public void SomeMethod(EventHandler handler) 
    { 
     handler += (s, e) => { /* Handler Code */ }; 
    } 

    public void SomeOtherMethod() 
    { 
     SomeMethod(Application.Current.Activated); 
    } 

Если я пытаюсь что я получаю ошибку:

The event 'Application.Activated' can only appear on the left hand side of += or -=

Это должно позволить вам знать, почему вы не можете сделать var appActivated = Observable.FromEvent(Application.Current.Activated);.

Итак, как я могу обойти это, чтобы присоединить события в SomeMethod?

Вот как:

public void SomeMethod(Action<EventHandler> addHandler) 
    { 
     addHandler((s, e) => { /* Handler Code */ }); 
    } 

    public void SomeOtherMethod() 
    { 
     SomeMethod(h => Application.Current.Activated += h); 
    } 

В принципе, в методе SomeMethod параметр больше не EventHandler, но Action<EventHandler>. Это означает, что я больше не пытаюсь передать это событие - вместо этого я передаю способ для вызываемого кода присоединяться к моему событию. h в призыве к SomeMethod является обещанием, что в будущем, если бы у меня был действительный обработчик, я могу прикрепить его, вызвав Action<EventHandler>.

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

public void SomeMethod(Action<EventHandler> addHandler, Action<EventHandler> removeHandler) 
    { 
     EventHandler handler = (s, e) => { /* Handler Code */ }; 

     addHandler(handler); 

     /* Some Intervening Code */ 

     removeHandler(handler); 
    } 

    public void SomeOtherMethod() 
    { 
     SomeMethod(h => Application.Current.Activated += h, h => Application.Current.Activated -= h); 
    } 

В /* Some Intervening Code */ код прилагается обработчик, и после того, как он отсоединяется.

Это подводит нас к коду в вашем вопросе:

var appActivated = Observable.FromEvent(
h => Application.Current.Activated += h, 
h => Application.Current.Activated -= h); 

Это очень такой же, как SomeMethod вызова выше - FromEvent нужен способ для его присоединения и отсоединения от события. h - это обещание, которое говорит «эй, FromEvent, если вы можете предоставить обработчик, когда вам это понадобится в будущем, я обещаю, что этот код будет правильно его прикреплять». Или, отсоедините, в зависимости от обстоятельств.

Теперь, просто чтобы быть немного педантичный, ваш код должен быть на самом деле:

 IObservable<EventPattern<EventArgs>> appActivated = 
      Observable 
       .FromEventPattern<EventHandler, EventArgs>(
        h => Application.Current.Activated += h, 
        h => Application.Current.Activated -= h); 

Теперь, когда у меня есть IObservable<EventPattern<EventArgs>> я могу переписать SomeMethod принять это в качестве параметра и записать его так:

public IDisposable SomeMethod(IObservable<EventPattern<EventArgs>> appActivated) 
    { 
     return appActivated.Subscribe(ep => { /* Handler Code */ }); 
    } 

Теперь можно увидеть всю силу Rx. Метод .Subscribe не нуждается ни в каких ссылках на объект, содержащий исходный объект, но в конечном итоге он вызовет h => Application.Current.Activated += h и h => Application.Current.Activated -= h, чтобы отсоединить его по мере необходимости. Теперь я могу эффективно передавать события как первоклассные типы в .NET.