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

Joh*_*ger 7 c# events system.reactive

我正在努力将以下事件转换为IObservable:

public delegate void _dispSolutionEvents_OpenedEventHandler();
event _dispSolutionEvents_OpenedEventHandler Opened;
Run Code Online (Sandbox Code Playgroud)

该活动来自图书馆,所以我无法改变它.IObservable.FromEvent应该做的重载具有以下签名:

public static IObservable<Unit> FromEvent
    ( Action<Action> addHandler
    , Action<Action> removeHandler
    )
Run Code Online (Sandbox Code Playgroud)

所以我尝试转换这样的事件:

var opened = Observable.FromEvent
    ( h => _SolutionEvents.Opened += h
    , h => _SolutionEvents.Opened -= h
    );
Run Code Online (Sandbox Code Playgroud)

但是编译器并不喜欢_SolutionEvents.Opened += h_SolutionEvents.Opened += h因为

无法将类型'System.Action'隐式转换为'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
    )
Run Code Online (Sandbox Code Playgroud)

这个允许将操作转换为事件处理程序,但似乎只能使用TEventArgs.

Rx是否缺少适当的超载或我错过了什么?

Eni*_*ity 9

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

这样做:

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

我用这段代码测试了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();
    }
}
Run Code Online (Sandbox Code Playgroud)

它产生以下预期输出:

Opened

值得注意的是,没有IObservable界面,只有一个,IObservable<T>所以你必须返回一些东西.这里的诀窍是转换delegate void _dispSolutionEvents_OpenedEventHandler()IObservable<Unit>使其工作,这就是它的h => () => h(Unit.Default)作用.


Lee*_*ell 5

您在这里遇到了类型问题。类型_dispSolutionEvents_OpenedEventHandler不是Action。看起来像Action类型,但其实不是类型Action

IMO 此事件不符合 .NET 事件标准。object通常,委托将匹配采用发送者参数和EventArg第二个参数的子类的模式。

IE。

public delegate void _dispSolutionEvents_OpenedEventHandler(object sender, EventArgs e);
Run Code Online (Sandbox Code Playgroud)

如果您尝试将 an 附加Action到事件中,您也会发现失败。

Action onOpened = ()=>Console.WriteLine("Opened");
_SolutionEvents.Opened += onOpened;  //CS0029 Cannot implicitly convert type 'System.Action' to '_dispSolutionEvents_OpenedEventHandler'
Run Code Online (Sandbox Code Playgroud)

如果你这样做,编译器足够聪明,可以进行一些类型推断;

_SolutionEvents.Opened+= () => Console.WriteLine("Opened");
Run Code Online (Sandbox Code Playgroud)

但是当您使用 Rx 时,您已经输入了类型Action,因此实际上回到了上面的上一个问题。

如果库所有者很好,事件将遵循正常的发送者/事件参数模式。如果做不到这一点,他们至少会将委托指定为一个Action而不是他们自己的客户无参数、无效方法。:-/

因此,由于您所拥有的事件不符合标准 .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);
});
Run Code Online (Sandbox Code Playgroud)