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