【问题标题】:Convert event without EventArgs using Observable.FromEvent使用 Observable.FromEvent 转换没有 EventArgs 的事件
【发布时间】:2016-03-08 06:24:16
【问题描述】:

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

这个允许将动作转换为事件处理程序,但它似乎只适用于TEventArgs

Rx 是否缺少适当的重载或我是否缺少某些内容?

【问题讨论】:

    标签: c# events system.reactive


    【解决方案1】:

    事实证明,使用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();
        }
    }
    

    它产生以下预期输出:

    打开

    值得注意的是,没有IObservable接口,只有IObservable&lt;T&gt;,所以你必须返回一些东西。这里的技巧是将delegate void _dispSolutionEvents_OpenedEventHandler() 转换为IObservable&lt;Unit&gt; 以使其工作,这就是h =&gt; () =&gt; h(Unit.Default) 所做的。

    【讨论】:

    • 啊,我明白了,谢谢。并且它还应该在您处理订阅时正确删除事件处理程序。
    • @JohannesEgger - 是的,当 订阅被处理 可观察对象自然结束时,它将正确删除处理程序。因此,例如,如果您将 .Take(1) 添加到可观察对象,那么它会在生成一个值后立即删除处理程序。
    • 只是对处置的澄清;当可观察序列自然结束时,订阅将被释放,这就是它会正确删除事件处理程序的原因。即当序列终止(OnError/OnComplete)时,任何订阅都会被释放。独立地,处置语义将删除处理程序。
    • @LeeCampbell - “独立地,处理语义将删除处理程序”是什么意思?您是指明确处置还是自然结束?
    【解决方案2】:

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

    【讨论】:

    • 感谢您的解释。无论如何,Rx 似乎能够处理非标准事件(使用FromEvent),所以我认为它应该适用于 any 非标准事件。所以我猜@Enigmativity 的答案是 Rx 精神中的答案。但你的也是一个很好的解决方案,谢谢。
    • 是的,他是一个很好的答案(赞成)。如果您对垃圾/分配有疑虑,请重新考虑使用 FromEventXXX 方法,因为它们可能会不必要地分配繁重 - 请参阅 github.com/LeeCampbell/RxCookbook/blob/master/Model/…
    • 这很有趣。谢谢你。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 2022-12-10
    • 2010-12-05
    相关资源
    最近更新 更多