Rx:基于Timer的IObservable与其他IObservable的不同TakeUntil行为

时间:2018-02-27 02:43:46

标签: c# system.reactive

我在使用TakeUntil和IObservable的不同实现作为参数时会遇到不同的行为。我试图理解为什么我会得到不同的行为。使用Linqpad:

async Task Main()
{
    var byTimer = true;

    var ending = Observable.FromEvent<long>(
        handler => endingHandler += handler,
        handler => endingHandler -= handler);

    var s = new Subject<long>();

    using (var disposibleSub =
        Observable
            .Interval(TimeSpan.FromSeconds(.2))
            .TakeUntil(byTimer ? Observable.Timer(TimeSpan.FromSeconds(1.5)) : ending)
            .DumpLatest()
            .Subscribe(Observer.Checked(s)))
    {
        if (endingHandler != null)
        {
            int r = Console.Read();
            endingHandler?.Invoke(r);
        }
        var v = await s.Count();
        Console.WriteLine("Count of items: {0}", v);
    }
}

public event Action<long> endingHandler;

具有计时器Count的那个总是返回正确的值。但是,如果我将其更改为使用FromEvent实现,我总是得到0.显然不同之处在于两者的实现。我也尝试使用TakeUntil的Subject实现,其结果与fromEvent相同。

计时器结果符合我的预期。

解释为什么会受到赞赏!感谢。

1 个答案:

答案 0 :(得分:1)

byTimertrue时,ending observable永远不会获得订阅 - 请记住,只有在订阅到达时才会实例化可观察的管道 - 所以在这种情况下{{1}附加事件代码不会运行,因此handler => endingHandler += handlerendingHandler。这意味着null未被调用,因此代码会立即降至Console.Read(),然后会捕获通过var v = await s.Count();的所有值。

但是,当sbyTimer时,false endingHandler,则会调用null。读取控制台后,立即调用Console.Read()来停止观察,并在主题上调用endingHandler。所以当它命中OnCompleted时,它立即得到一个完整的信号,错过了所有先前产生的值,因此你得到的数量为零。

如果您将代码更改为:

var v = await s.Count();

然后两个可观测量的行为完全相同。

如果您还要将 int r = Console.Read(); if (endingHandler != null) { endingHandler?.Invoke(r); } var v = await s.Count(); 更改为Subject,那么代码的行为应该与您原来期望的一样。