Преобразование события без 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.Действие" to " 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)
.
Здесь вы сталкиваетесь с проблемой типа А. Тип
_dispSolutionEvents_OpenedEventHandler
не являетсяAction
. Он выглядит как ТипAction
, но это не ТипAction
.IMO это событие не соответствует стандартам .NET для событий. В общем случае делегат будет соответствовать шаблону принятия параметра sender
object
и подклассаEventArg
для второго параметра.Т. е.
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
, поэтому эффективно возвращаетесь к предыдущему вопросу выше.Если владелец библиотеки был хорошим, событие будет следовать обычному шаблону sender/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); });