Наблюдения - это первоклассные типы в .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.
Я написал длинное объяснение и обсуждение FromEvent некоторое время назад, которое могло бы быть полезным: http://stackoverflow.com/questions/19895373/how-to-use-observable-fromevent-instead-of-fromeventpattern-and -avoid-string-lit/19896246 # 19896246 –
@James World: У меня было 60-70% понимания после чтения ответов здесь. Это> 95% после прочтения вашего сообщения. Особенно ваш абзац начинается с «Обратите внимание, что это только тот акт вызова Subscribe, который создает подписку». Мне очень помогли. – Bad
Спасибо за отзыв. Это удивительно глубокий метод и требует много объяснений! –