'冲洗'可观察的扫描

时间:2014-11-20 14:38:32

标签: c# system.reactive

这是一个奇怪的“问题”,我不确定处理它的最佳方法是什么。

为了简化,假设我有一个可观察的来源,其中一些数据来自“外部”:

{ Value, TimeStamp }

我通过Observable.Scan将其放入以便输出:

{ Value, TimeStamp, TimeDelta }

这意味着我的数据总是“迟到”,但这不是问题。

我们正在从这个观察中“录制”,当你停止一次录制时,仍有一个数据值“卡住”等待它的追随者。

即使这不是问题。问题是,当你再次开始录制时,前一个“录制”的最后一个值会卡在新录制的开头。

最明显的事情就是取消订阅和重新订阅,但......并不是那么简单,因为这个扫描的源不仅会被记录,还会被发送到用户界面,并用于进一步的计算。 :所以我必须做一个巨大的取消订阅/重新订阅。

我正在考虑一种注入某种“重置”数据的方法,但不确定如何将信息发送回“可观察流”......

也许我咬的比我咬的还多?或者使用过多的Observable?

2 个答案:

答案 0 :(得分:1)

有很多方法可以做到这一点,但一个相当容易的方法是使用.Switch()运算符。

它基本上是这样的:如果你有IObservable<IObservable<T>>,你可以调用.Switch()把它变成IObservable<T>,它基本上订阅外部可观察量产生的最后一个值并取消订阅以前制作的可观察资料。

现在听起来有点时髦,但这是它的工作原理。假设您有一个名为outsideObservable的可观察对象,那么您定义了第二个可观察的(resubscribeObservable),每次要重新订阅时都会生成一个值,并且您按照这样订阅它们:

var subscription =
    resubscribeObservable
        .Select(_ => outsideObservable)
        .Switch()
        .Subscribe(x =>
        {
            /* Do stuff here */
        });

现在要重新订阅outsideObservable,您只需要从resubscribeObservable生成一个值。

最简单的方法是将其定义为var resubscribeObservable = new Subject<Unit>();,然后在每次要重新订阅时调用resubscribeObservable.OnNext(Unit.Default);

或者,如果您有某些事件,例如用户点击按钮,那么您可以将基于该事件的观察值用作resubscribeObservable

整合评论中的建议,这看起来像是:

var factory = Observable.Defer(() => outsideObservable);

var resetterObservable = new Subject<Unit>();

var resettableObservable = 
       resetterObservable
           .StartWith(Unit.Default)
           .Select(_ => factory)
           .Switch()
           .Publish()
           .RefCount();

Publish()。RefCount()只是为了保护outsideObservable免受多个同时订阅的影响。

答案 1 :(得分:0)

这是我已经接受的答案。尚未投入生产,但测试似乎表明它符合我的要求。

public interface IResetter
{
    IObservable<T> MakeResettable<T>(Func<IObservable<T>> selector);
}
public class Resetter : IResetter
{
    private Subject<Unit> _Resetter = new Subject<Unit>();
    public void Reset()
    {
        _Resetter.OnNext(Unit.Default);
    }
    public IObservable<T> MakeResettable<T>(Func<IObservable<T>> selector)
    {
        return
            _Resetter
                .StartWith(Unit.Default)
                .Select(_ => Observable.Defer(selector))
                .Switch()
                .Publish().RefCount();
    }
}