使用 Observable.FromEvent 转换没有 EventArgs 的事件

Convert event without EventArgs using 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因为

Cannot implicitly convert type 'System.Action' 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 是否缺少适当的重载或者我是否遗漏了什么?

您 运行 遇到了类型问题。 _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);
});

事实证明,使用 FromEvent 模式非常容易。

只需这样做:

var opened = Observable.FromEvent<_dispSolutionEvents_OpenedEventHandler, Unit>(
    h => () => h(Unit.Default),
    h => _SolutionEvents.Opened += h,
    h => _SolutionEvents.Opened -= h);

我已经用这段代码测试了 observable:

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>所以你必须return一些东西。这里的技巧是将 delegate void _dispSolutionEvents_OpenedEventHandler() 转换为 IObservable<Unit> 以使其工作,这就是 h => () => h(Unit.Default) 所做的。