Вот то, что я имею в виду. Предположим, что я работаю с API, который выставляет события, но эти события не следуют стандарту EventHandler
или EventHandler<TEventArgs>
подпись. Одно событие могло бы быть похожим на это, например:
Public Event Update(ByVal sender As BaseSubscription, ByVal e As BaseEvent)
Теперь, обычно, если я хочу добраться IObservable<TEventArgs>
от события я могу просто сделать это:
Dim updates = Observable.FromEvent(Of UpdateEventArgs)( _
target:=updateSource, _
eventName:="Update" _
)
Но это не работает, потому что Update
событие не EventHandler<UpdateEventArgs>
- на самом деле, существует нет UpdateEventArgs
- это - в основном просто своя собственная вещь.
Очевидно, я мог определить свой собственный класс, происходящий из EventArgs
(т.е. UpdateEventArgs
), запишите другой класс для обертывания объекта, обеспечивающего Update
событие, дайте классу обертки его собственное Update
событие, которое является EventHandler<UpdateEventArgs>
, и доберитесь IObservable<UpdateEventArgs>
от этого. Но это - раздражающий объем работы.
Есть ли некоторый способ создать IObservable<[something]>
от "нестандартного" события как это, или я не повезло?
ОБНОВЛЕНИЕ: Из ответа Jon Skeet меня пошагово перемещают в направлении следующей перегрузки Observable.FromEvent
:
Function FromEvent(Of TDelegate, TEventArgs As EventArgs)( _
conversion As Func(Of EventHandler(Of TEventArgs), TDelegate), _
addHandler As Action(Of TDelegate), _
removeHandler As Action(Of TDelegate) _
) As IObservable(Of IEvent(Of TEventArgs))
Я должен признать, тем не менее, что я испытываю затруднения при обертывании моей головы вокруг этого Func(Of EventHandler(Of TEventArgs), TDelegate)
часть. Это кажется назад мне (?). Очевидно, существует только что-то, что я пропускаю...
Так или иначе, в случае, если это помогает, я думаю, что это - то, на что эквивалентный код C# был бы похож (я буду совершенно честен: я не уверен в этом. Даже при том, что я обычно предпочитаю C# сам, этот код является работой одного из моих коллег, который пишет, прежде всего, в VB.NET; и VB.NET разрешает несколько синтаксисов для объявления событий):
// notice: not an EventHandler<TEventArgs>
public delegate void UpdateEventHandler(BaseSubscription sender, BaseEvent e);
// not 100% sure why he did it this way
public event UpdateEventHandler Update;
Хитрая часть здесь - то, что кажется что некоторый класс, происходящий из EventArgs
необходимо, несмотря ни на что. В API я работаю с, нет такого класса. Так, абсолютный минимум, я должен буду записать тот. Но это должно быть довольно тривиально (в основном одно свойство: BaseEvent
).
В конце я предполагаю, что код, требуемый для этой перегрузки, выглядел бы примерно так в C#:
var updates = Observable.FromEvent<UpdateEventHandler, UpdateEventArgs>(
// conversion (Func<EventHandler<UpdateEventArgs>, UpdateEventHandler>)
handler => (sender, e) => handler(sender, new UpdateEventArgs(e)),
// addHandler (Action<UpdateEventHandler>)
handler => updateSource.Update += handler,
// removeHandler (Action<UpdateEventHandler>)
handler => updateSource.Update -= handler
);
В первую очередь: у меня даже есть это прямо? Второй из всех: я корректен в высказывании что использование VB 9, нет действительно никакого способа выполнить вышеупомянутое, не пишущий мои собственные методы?
Это почти чувствует мне как, я приближаюсь к этой проблеме от совершенно неправильного угла для начала. Но я действительно не уверен.
Вы можете использовать эту подпись:
Public Shared Function FromEvent(Of TDelegate, TEventArgs As EventArgs) ( _
conversion As Func(Of EventHandler(Of TEventArgs), TDelegate), _
addHandler As Action(Of TDelegate), _
removeHandler As Action(Of TDelegate) _
) As IObservable(Of IEvent(Of TEventArgs))
Здесь TDelegate
будет типом делегата события (что я могу ' Сразу видно из вашего объявления - объявления событий C # выглядят не совсем так, и, боюсь, я не знаю достаточно VB, чтобы расшифровать их, но я уверен, что где-то там есть тип делегата). TEventArgs
будет типом для аргумента события (я думаю, BaseEvent
должен сделать это здесь).Вам нужно будет предоставить преобразователь из EventHandler (Of BaseEvent)
в ваш тип делегата - это, вероятно, будет просто лямбда-выражением для вызова данного обработчика событий с переданными в него аргументами. Действия добавления и удаления будут обычным кодом подписки на событие, но выражены в виде делегатов.
К сожалению, мой VB недостаточно хорош, чтобы выразить все это аккуратно - или действительно знать, сколько легко доступно в VB 9 или 10. Я знаю, как все это будет выглядеть на C # ... если бы вы могли дать мне короткий, но полный пример на C #, который просто оставил мне заполнить бит подписки, я определенно мог бы это сделать ...
Возможно, вы могли бы просто добавить свою собственную реализацию для настраиваемой сигнатуры события?
public interface ICustomEvent<TSource, TArgs>
{
public TSource Source { get; }
public TArgs EventArgs { get; }
}
public interface CustomEvent<TSource, TArgs> : ICustomEvent<TSource, TArgs>
{
public TSource Source { get; set; }
public TArgs EventArgs { get; set; }
}
public static class ObservableEx
{
public static IObservable<ICustomEvent<TSource, TArgs>> FromCustomEvent(
Action<Action<TSource, TArgs>> addHandler,
Action<Action<TSource, TArgs>> removeHandler)
{
return Observable.CreateWithDisposable(observer =>
{
Action<TSource, TArgs> eventHandler = (s,a) =>
observer.OnNext(new CustomEvent<TSource,TArgs>(s,a));
addHandler(eventHandler);
return Disposable.Create(() => removeHandler(eventHandler));
});
}
}
Тогда вы можете использовать ее как:
var observable = ObservableEx.FromCustomEvent<BaseSubscription,BaseEvent>(
h => updateSource.Update += h,
h => updateSource.Update -= h
);
Вы также можете просто сделать это ленивым способом, если updateSource никогда не исчезнет:
var observable = new Subject<BaseEvent>();
updateSource.Update += (o,e) => observable.OnNext(e);
План Джона, вероятно, лучше, но Субъекты могут вам помочь.