C# RX Switch()的订阅和取消订阅订单

C# RX Switch()的订阅和取消订阅订单,c#,.net,system.reactive,reactive-programming,C#,.net,System.reactive,Reactive Programming,所以我写了一份我遇到的问题的草稿。我有一个IObservable包含我的流,我想使用switch从中获取最新的项目,但是我的问题可以通过下面的代码清楚地演示: var obs = Observable.Create<IObservable<int>>(sub => { var item = Observable.Create<int>(innersub => {

所以我写了一份我遇到的问题的草稿。我有一个IObservable>包含我的流,我想使用switch从中获取最新的项目,但是我的问题可以通过下面的代码清楚地演示:

        var obs = Observable.Create<IObservable<int>>(sub =>
        {
            var item = Observable.Create<int>(innersub =>
            {
                var count = 0;
                return Observable.Interval(TimeSpan.FromSeconds(2)).Subscribe(x => innersub.OnNext(count++));
            }).Publish().RefCount();

            return Observable.Interval(TimeSpan.FromSeconds(10)).Subscribe(x => sub.OnNext(item));
        });

        obs.Switch().Subscribe(x => Console.WriteLine(x));
上面的测试用例显示,当与Publish.RefCount结合使用时,交换机首先取消订阅,然后订阅新项目

我想要的是一个连续的数字流上升,但测试显示,该项目是在新的订阅命中前首先处理,我失去了计数,必须重新开始

如果项目是相同的,并且使用了refcount,那么我希望先进行订阅,这样refcount会很高兴,然后处理旧订阅。这是RX默认可以演示的行为,还是需要一些预兆才能正确?我相信我可以基于RX源代码的精简版本编写一个足够简单的扩展方法,但如果它已经存在,或者有更好的方法,我想先知道


编辑:编写的代码是一个简单的示例,用一种简单的方式演示了这个问题。我实际拥有的是一个定期发布一个新的可观察对象的可观察对象,它有不同的过滤器,但最终归结为相同的发布/引用计数可观察对象。where子句更改,或者select执行不同的操作。真正的用途是将几个流合并,所以我对我的逻辑和我对问题的结论很有信心。我很清楚,我的示例可以简化。

您必须查看源,因为前一个可观察对象在当前可观察对象被订阅之前被释放。这就是开关的工作原理

如果在新订阅后处理Rx,则代码的意图似乎等同于简单地执行此操作:

var obs = Observable.Create<int>(innersub =>
{
    var count = 0;
    return Observable.Interval(TimeSpan.FromSeconds(2))
        .Subscribe(x => innersub.OnNext(count++));
});

obs.Subscribe(x => Console.WriteLine(x));

也许您可以让我们知道您的基本需求是什么,我们可以对此进行研究?

此操作符主要用于冷观测。在订阅新的可观察对象之前,切换取消订阅上一个可观察对象。否则,将存在一种竞赛条件,即额外的赛事可能会在双方都订阅的短暂时间内溜走

因为您的基础可观察是热的,您可能会考虑另一种解决方案,在这里只需修改过滤器/SELECT,而不是使用Switter重新订阅。比如:

source
    .Where(t => ApplyCurrentFilter(t))
    .Select(t => ApplyCurrentProjection(t))
    .Subscribe(...);

// do something that changes what `ApplyCurrentFilter` does...

我不知道这比您当前的解决方案好还是坏,但它确实避免了从源数据取消订阅/重新订阅的需要。

如前所述,Observable.Create生成一个cold Observable,Publish.RefCount只会在仍有订户的情况下使其变热。在处理旧的订阅服务器之前,可以编写订阅新订阅服务器的交换机的您自己的版本。但我会对比赛条件非常谨慎。总的来说,这样做感觉有点奇怪,在Rx中通常表示有另一种方式可以做你想做的事情,更干净

在本例中,如果您获得了所需的结果,那么发布多个可观察对象并切换它们是没有意义的,因为实际上您只希望在整个持续时间内订阅一个可观察对象。因此,它如何归结为什么谜说

然而,很明显,这是一个人为的例子,所以让我们假设有一个更复杂的情况需要这种方法——如果您能够详细说明,它可能会有所帮助。从这个例子来看,似乎你只想订阅一次,永远的内在可观察性。基于这一要求,RefCount是不合适的,但我假设您使用它是因为您希望在核心处有一个共享的可观察对象,您正在与其他操作符包装,您希望每次都采取不同的操作。如果是这种情况,您可以使用如下方法:

var obs = Observable.Create<IObservable<int>>(sub =>
{
   var item = Observable.Create<int>(innersub =>
   {
       var count = 0;
       return Observable.Interval(TimeSpan.FromSeconds(2))
                        .Subscribe(x => innersub.OnNext(count++));
   }).Publish();

   bool connected = false;
   var disposables = new CompositeDisposable();
   disposables.Add(Observable.Interval(TimeSpan.FromSeconds(10))
                             .Subscribe(x =>
                             {
                                 // push the new stream to the observer first
                                 sub.OnNext(item);

                                 if (!connected)
                                 {
                                     connected = true;
                                     disposables.Add(item.Connect());
                                 }
                             }));

   return disposables;
});
我还没有考虑过这种方法的潜在比赛条件等,这在很大程度上取决于你的实际情况。然而,在原始帖子的基本测试中,这似乎是您想要的

var obs = Observable.Create<IObservable<int>>(sub =>
{
   var item = Observable.Create<int>(innersub =>
   {
       var count = 0;
       return Observable.Interval(TimeSpan.FromSeconds(2))
                        .Subscribe(x => innersub.OnNext(count++));
   }).Publish();

   bool connected = false;
   var disposables = new CompositeDisposable();
   disposables.Add(Observable.Interval(TimeSpan.FromSeconds(10))
                             .Subscribe(x =>
                             {
                                 // push the new stream to the observer first
                                 sub.OnNext(item);

                                 if (!connected)
                                 {
                                     connected = true;
                                     disposables.Add(item.Connect());
                                 }
                             }));

   return disposables;
});