Преобразование события без 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); });