简体   繁体   English

在 C# 中使用 RX 获取可观察序列中的最新项目

[英]Getting the latest item in an observable sequence using RX in C#

take the following as an example:以以下为例:

var ob = Observable.Interval(TimeSpan.FromSeconds(1)).StartWith(500).Replay(1).RefCount();

What I'm trying to achieve here is to obtain the value of the latest item in the sequence at any given time "synchronously".我在这里试图实现的是在任何给定时间“同步”获取序列中最新项目的值。 Which means extensions like FirstAsync can't make it up for me.这意味着像FirstAsync这样的扩展无法弥补我的FirstAsync

The StartWith and Replay bit ensures that there will always be a value, and the RefCount bit is necessary in my actual code to detect when I can do some disposal actions. StartWithReplay位确保总是有一个值,并且在我的实际代码中需要RefCount位来检测我何时可以执行一些处理操作。

So to simulate this "any given time" part, let's try getting the latest value after 5 seconds:所以为了模拟这个“任何给定时间”的部分,让我们尝试在 5 秒后获取最新值:

Observable.Timer(TimeSpan.FromSeconds(5)).Subscribe(x =>
{
    // Try to get latest value from "ob" here.
});

So with a 5 second delay, I need to get the value 5 out of the sequence and these are what I have tried so far with no success:所以有 5 秒的延迟,我需要从序列中获取值5 ,这些是我迄今为止尝试过但没有成功的方法:

  1. ob.First() - returns 500 ob.First() - 返回 500
  2. ob.Latest().Take(1) - same as above ob.Latest().Take(1) - 同上
  3. ob.MostRecent(-1).First() - same as above ob.MostRecent(-1).First() - 同上
  4. ob.MostRecent(-1) - gives me an IEnumerable<long> full of "500" ob.MostRecent(-1) - 给我一个IEnumerable<long>充满“500”
  5. ob.Last() - never returns because it's waiting for the sequence to complete which it never will ob.Last() - 永远不会返回,因为它正在等待它永远不会完成的序列
  6. ob.Latest().Last() - same as above ob.Latest().Last() - 同上
  7. ob.ToTask().Result - same as above ob.ToTask().Result - 同上
  8. ob.ToEnumerable() - same as above ob.ToEnumerable() - 同上
  9. ob.MostRecent().Last() same as above ob.MostRecent().Last()同上

It seems there's not much resources around that people can actually do this.似乎没有太多资源可以让人们真正做到这一点。 The closest I can find is this: " Rx: operator for getting first and most recent value from an Observable stream ", but it is not a synchronous call after all (still using a subscription) so it doesn't work for me.我能找到的最接近的是:“ Rx:用于从 Observable 流中获取第一个和最新值的操作符”,但它毕竟不是同步调用(仍然使用订阅),所以它对我不起作用。

Does any body know if this is actually doable?有没有人知道这是否真的可行?

To point out why your code probably isn't working as you expect it to指出为什么你的代码可能没有像你期望的那样工作

var ob = Observable.Interval(TimeSpan.FromSeconds(1)).StartWith(500).Replay(1).RefCount();
//Note at this point `ob` has never been subscribed to,
// so the Reference-count is 0 i.e. has not be connected.

Observable.Timer(TimeSpan.FromSeconds(5)).Subscribe(x =>
{
    // Try to get latest value from "ob" here.

    //Here we make our first subscription to the `ob` sequence.
    //  This will connect the sequence (invoke subscribe)
    //   which will
    //      1) invoke StartWith
    //      2) invoke onNext(500)
    //      3) invoke First()
    //      4) First() will then unsubscribe() as it has the single value it needs
    //      5) The refCount will now return to 0
    //      6) The sequence will be unsubscribed to.
    ob.First().Dump();  

    //Any future calls like `ob.First()` will thus always get the value 500.
});

Potentially what you want is可能你想要的是

var ob = Observable.Interval(TimeSpan.FromSeconds(1))
    .Publish(500);
var connection = ob.Connect();
//Note at this point `ob` has never been subscribed to, so the ReferenceCount is 0 i.e. has not be connected.

var subscription = Observable.Timer(TimeSpan.FromSeconds(5)).Subscribe(x =>
{
    // Try to get latest value from "ob" here.
    ob.First().Dump();
});

//Sometime later
subscription.Dispose();
connection.Dispose()

HOWEVER, You really don't want to be mixing Synchronous calls with Rx.但是,您真的不想将同步调用与 Rx 混合在一起。 You also generally don't want to be subscribing within a subscription (as .First() is a subscription).您通常也不想在订阅中订阅(因为.First()是订阅)。 What you probably mean to be doing is getting the latest value, and stashing it somewhere.您可能想要做的是获取最新值,并将其存放在某处。 Using .First() is just a slippery slope.使用.First()只是一个滑坡。 You probably would be better writing something like你可能会更好地写一些类似的东西

var subscription = Observable.Timer(TimeSpan.FromSeconds(5))
    .SelectMany(_=>ob.Take(1))
    .Subscribe(x =>
    {
        //Do something with X here.
        x.Dump();
    }); 

You need to do something like this:你需要做这样的事情:

var ob = Observable.Interval(TimeSpan.FromSeconds(1)).StartWith(500);

var latestAndThenTheRest =
    Observable
        .Create<long>(o =>
        {
            var bs = new BehaviorSubject<long>(1);
            var s1 = ob.Subscribe(bs);
            var s2 = bs.Subscribe(o);
            return new CompositeDisposable(s1, s2);
        });

The only thing that you need to consider here is that ob must be a hot observable for this to even make sense.在这里你唯一需要考虑的是ob必须是一个热门的 observable 才能让它有意义。 If it were cold then every subscriber would get a brand new subscription to the start of the ob sequence.如果天气很冷,那么每个订阅者都会在ob序列的开头获得一个全新的订阅。

Just to clarify this a bit, and thanks for @LeeCampbell's answer.只是为了澄清这一点,并感谢@LeeCampbell 的回答。

What was not working:什么不起作用:

var ob = Observable.Interval(TimeSpan.FromSeconds(1)).StartWith(500).Replay(1).RefCount();
Observable.Timer(TimeSpan.FromSeconds(5)).Subscribe(x =>
{
    ob.First().Dump();
    // This gives you 500.
    // Because this is the first time any one subscribes to the observable,
    // so it starts right here and gives you the initial value.
});

What would actually work:什么会真正起作用:

var ob = Observable.Interval(TimeSpan.FromSeconds(1)).StartWith(500).Replay(1).RefCount();
ob.Subscribe(); // Subscribe to start the above hot observable immediately.
Observable.Timer(TimeSpan.FromSeconds(5)).Subscribe(x =>
{
    ob.First().Dump(); 
    // This would give you either 3 or 4, depending on the speed and timing of your computer.
});

I'm not sure if this answer helps you, but have you looked into BehaviorSubject?我不确定这个答案是否对你有帮助,但你有没有研究过 BehaviorSubject? It's an IObservable that remembers its latest value.它是一个 IObservable,可以记住它的最新值。 It's a bit like a combination of a regular variable and an observable in one.这有点像将常规变量和可观察对象合二为一。

Otherwise, why don't you subscribe to 'ob' and store the latest value in a variable yourself?否则,您为什么不订阅 'ob' 并自己将最新值存储在变量中?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM