我正在努力将以下事件转换为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是否缺少适当的重载或我错过了什么?
答案 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);
});