Преобразование события без EventArgs с помощью Observable.FromEvent
Я борюсь с преобразованием следующего события в IObservable
:
public delegate void _dispSolutionEvents_OpenedEventHandler();
event _dispSolutionEvents_OpenedEventHandler Opened;
событие происходит из библиотеки, поэтому я не могу его изменить.
Перегрузка IObservable.FromEvent
что должен сделать это следующим образом:
public static IObservable<Unit> FromEvent
( Action<Action> addHandler
, Action<Action> removeHandler
)
поэтому я попытался преобразовать событие следующим образом:
var opened = Observable.FromEvent
( h => _SolutionEvents.Opened += h
, h => _SolutionEvents.Opened -= h
);
но компилятору не нравится _SolutionEvents.Opened += h
и _SolutionEvents.Opened += h
, потому что
не удается неявно преобразовать тип System.Действие на 'Автоматизации EnvDTE._dispSolutionEvents_OpenedEventHandler'.
Я не думаю, что могу просто сказать_SolutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(h)
потому что тогда удаление не будет работать, потому что у меня другой экземпляр, верно?
есть еще одна перегрузка Observable.FromEvent
со следующей подписью:
public static IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>
( Func<Action<TEventArgs>, TDelegate> conversion
, Action<TDelegate> addHandler
, Action<TDelegate> removeHandler
)
это позволяет преобразовать действие в обработчик событий, но, похоже, работает только с TEventArgs
.
Rx отсутствует соответствующая перегрузка или мне не хватает что-то?
2 ответов
это получается, что это очень легко использовать FromEvent
узор.
просто сделать это:
var opened = Observable.FromEvent<_dispSolutionEvents_OpenedEventHandler, Unit>(
h => () => h(Unit.Default),
h => _SolutionEvents.Opened += h,
h => _SolutionEvents.Opened -= h);
Я проверил наблюдаемое с помощью этого кода:
void Main()
{
var _SolutionEvents = new Foo();
var opened = Observable.FromEvent<_dispSolutionEvents_OpenedEventHandler, Unit>(h => () => h(Unit.Default), h => _SolutionEvents.Opened += h, h => _SolutionEvents.Opened -= h);
opened.Subscribe(x => Console.WriteLine("Opened"));
_SolutionEvents.OnOpened();
}
public delegate void _dispSolutionEvents_OpenedEventHandler();
public class Foo
{
public event _dispSolutionEvents_OpenedEventHandler Opened;
public void OnOpened()
{
this.Opened?.Invoke();
}
}
он производит следующий ожидаемый результат:
Opened
стоит отметить, что нет IObservable
интерфейс, но только IObservable<T>
так что вы должны что-то вернуть. Фокус здесь в том, чтобы преобразовать delegate void _dispSolutionEvents_OpenedEventHandler()
на IObservable<Unit>
чтобы заставить его работать и что h => () => h(Unit.Default)
делает.
вы столкнулись с проблемой типа A здесь. The _dispSolutionEvents_OpenedEventHandler
тип Action
. Похоже на Action
тип, но это не Action
тип.
IMO это событие не соответствует стандартам .NET для событий. Как правило, делегат будет соответствовать шаблону принятия отправителя и EventArg
подкласс для второго параметра.
ie.
public delegate void _dispSolutionEvents_OpenedEventHandler(object sender, EventArgs e);
если вы пытаетесь просто прикрепить Action
в случае, если вы найдете это тоже не помогает.
Action onOpened = ()=>Console.WriteLine("Opened");
_SolutionEvents.Opened += onOpened; //CS0029 Cannot implicitly convert type 'System.Action' to '_dispSolutionEvents_OpenedEventHandler'
компилятор достаточно умен, чтобы сделать некоторый вывод типа, если вы это сделаете;
_SolutionEvents.Opened+= () => Console.WriteLine("Opened");
но когда вы используете Rx, вы уже набраны в Action
type, поэтому эффективно вернулись к предыдущей проблеме выше.
если владелец библиотеки был хорошим, событие будет следовать обычному шаблону отправителя / eventArgs. В противном случае они, по крайней мере, укажут делегата как просто Action
вместо их собственного клиента метод без параметров, void. :-/
Итак, поскольку событие, которое у вас есть, не соответствует стандартным шаблонам .NET, вам нужно будет предоставить Rx еще несколько рук (вините поставщика библиотеки, а не Rx).
вы могли бы бороться с FromEvent
/FromEventPattern
методы, но поскольку ваша библиотека не в духе события, я бы предложил просто пойти с простым использованием Observable.Create
который, по крайней мере, сохраняет код очевидным, что происходит, и должен позволить следующему пользователю лучше понять он.
Observable.Create<Unit>(obs =>
{
_dispSolutionEvents_OpenedEventHandler handler = () => obs.OnNext(Unit.Default);
_SolutionEvents.Opened += handler;
return System.Reactive.Disposables.Disposable.Create(() => _SolutionEvents.Opened -= handler);
});