簡體   English   中英

在 C# 中使用 RX 獲取可觀察序列中的最新項目

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

以以下為例:

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

我在這里試圖實現的是在任何給定時間“同步”獲取序列中最新項目的值。 這意味着像FirstAsync這樣的擴展無法彌補我的FirstAsync

StartWithReplay位確保總是有一個值,並且在我的實際代碼中需要RefCount位來檢測我何時可以執行一些處理操作。

所以為了模擬這個“任何給定時間”的部分,讓我們嘗試在 5 秒后獲取最新值:

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

所以有 5 秒的延遲,我需要從序列中獲取值5 ,這些是我迄今為止嘗試過但沒有成功的方法:

  1. ob.First() - 返回 500
  2. ob.Latest().Take(1) - 同上
  3. ob.MostRecent(-1).First() - 同上
  4. ob.MostRecent(-1) - 給我一個IEnumerable<long>充滿“500”
  5. ob.Last() - 永遠不會返回,因為它正在等待它永遠不會完成的序列
  6. ob.Latest().Last() - 同上
  7. ob.ToTask().Result - 同上
  8. ob.ToEnumerable() - 同上
  9. ob.MostRecent().Last()同上

似乎沒有太多資源可以讓人們真正做到這一點。 我能找到的最接近的是:“ Rx:用於從 Observable 流中獲取第一個和最新值的操作符”,但它畢竟不是同步調用(仍然使用訂閱),所以它對我不起作用。

有沒有人知道這是否真的可行?

指出為什么你的代碼可能沒有像你期望的那樣工作

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.
});

可能你想要的是

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()

但是,您真的不想將同步調用與 Rx 混合在一起。 您通常也不想在訂閱中訂閱(因為.First()是訂閱)。 您可能想要做的是獲取最新值,並將其存放在某處。 使用.First()只是一個滑坡。 你可能會更好地寫一些類似的東西

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

你需要做這樣的事情:

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);
        });

在這里你唯一需要考慮的是ob必須是一個熱門的 observable 才能讓它有意義。 如果天氣很冷,那么每個訂閱者都會在ob序列的開頭獲得一個全新的訂閱。

只是為了澄清這一點,並感謝@LeeCampbell 的回答。

什么不起作用:

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.
});

什么會真正起作用:

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.
});

我不確定這個答案是否對你有幫助,但你有沒有研究過 BehaviorSubject? 它是一個 IObservable,可以記住它的最新值。 這有點像將常規變量和可觀察對象合二為一。

否則,您為什么不訂閱 'ob' 並自己將最新值存儲在變量中?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM