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

时间:2016-03-08 06:24:16

标签: c# events system.reactive

我正在努力将以下事件转换为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,因为

  

无法隐式转换类型&#39; System.Action&#39; to&#39; EnvDTE._dispSolutionEvents_OpenedEventHandler&#39;。

我不认为我可以说_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 个答案:

答案 0 :(得分:9)

事实证明,使用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>,所以你必须返回一些内容。这里的诀窍是将delegate void _dispSolutionEvents_OpenedEventHandler()转换为IObservable<Unit>以使其有效,这就是h => () => h(Unit.Default)的作用。

答案 1 :(得分:4)

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

IMO此事件不符合事件的.NET标准。通常,委托将匹配为第二个参数采用发件人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);
});