Taking a snapshot of ReplaySubject<T> buffer

谁都会走 提交于 2021-02-19 08:35:14

问题


I have a large ReplaySubject that allows subscribers to receive the replay buffer and future notifications as normal. In addition, I would like to be able to take a "snapshot" of the current buffer and return that as a list synchronously, without having to subscribe.

Is there a way to do this?

thanks


回答1:


Can you not just subscribe, receive the items, then unsubscribe?

public static List<T> Snapshot<T>(ReplaySubject<T> subject)
{
    List<T> snapshot = new List<T>();
    using (subject.Subscribe(item => snapshot.Add(item))
    {
        // Deliberately empty; subscribing will add everything to the list.
    }
    return snapshot;
}

That's assuming that subscribing to a ReplaySubject<T> invokes the element handler synchronously, of course. You'd want to check that, but that's what I'd expect.

You should also consider whether you want to handle errors / completion somehow.




回答2:


ONLY because @PaulBetts said there's only one way to do it :)

note: I don't recommend this way; Use Skeet's method and you'll thank yourself later.

So the magic of a ReplaySubject<T> all has to do with the fact it queues up any values it receives via OnNext on an internal Queue<TimeInterval<T>>. So we can write a wrapper that fiddles around inside the private details of the replay subject to get that info:

public class FixedReplaySubject<T> : ISubject<T>
{
    private ReplaySubject<T> _inner;
    private Func<Queue<TimeInterval<T>>> _snapshotGetter;

    public FixedReplaySubject(ReplaySubject<T> source)
    {
        _inner = source;
        var expr = Expression.Lambda(
            typeof(Func<Queue<TimeInterval<T>>>), 
            Expression.Field(
                Expression.Constant(source), 
                source.GetType()
                    .GetField("_queue", BindingFlags.NonPublic|BindingFlags.Instance)));
        _snapshotGetter = (Func<Queue<TimeInterval<T>>>)expr.Compile();
    }

    public IEnumerable<TimeInterval<T>> Snapshot()
    {
        return _snapshotGetter();
    }

    public IDisposable Subscribe(IObserver<T> observer)
    {
        return _inner.Subscribe(observer);
    }
    public void OnNext(T value)
    {
        _inner.OnNext(value);
    }
    public void OnCompleted()
    {
        _inner.OnCompleted();
    }
    public void OnError(Exception error)
    {
        _inner.OnError(error);
    }
    public void Dispose()
    {
        _inner.Dispose();
    }
}

Test rig:

void Main()
{
    var src = new ReplaySubject<int>();
    src.OnNext(1);
    src.OnNext(2);
    src.OnNext(3);
    src.OnNext(4);
    src.OnNext(5);
    src.OnNext(6);
    var heh = new FixedReplaySubject<int>(src);
    heh.Snapshot().Dump();
}

Result: (TimeInterval<T> is just value + time it came in)

1 00:00:00.0010265 
2 00:00:00.0010278 
3 00:00:00.0010278 
4 00:00:00.0010282 
5 00:00:00.0010282 
6 00:00:00.0010286 


来源:https://stackoverflow.com/questions/15185863/taking-a-snapshot-of-replaysubjectt-buffer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!