如何让IObservable在订阅时推送最新值

Jes*_*det 5 c# system.reactive

通常,当您订阅值的更改时,您也有兴趣知道初始值.我希望我的IObservable缓存最新(或初始)值并在订阅时推送该值.

当使用普通事件时,我经常会得到看起来像的代码

x.SomeEvent += SomeEventHandler;
SomeEventHandler(x, EventArgs.Empty);
Run Code Online (Sandbox Code Playgroud)

使用IObservable我希望用推送初始值的东西包装事件.如果我有多个订阅者,他们应该在订阅时收到最新的值我有一些代码,如果我在创建IObservable之后立即订阅,但是如果事件在订阅之前触发则不会:

class Program
{
    static void Main()
    {
        var s = new Source { Value = 1 };
        var values = Observable.Return(s.Value).Concat(
            Observable.FromEvent(
                h => s.ValueChanged += h,
                h => s.ValueChanged -= h)
            .Select(_ => s.Value));
        using (values.Subscribe(Console.WriteLine))
        {
            s.Value = 2; // prints 1,2 as expected
        }
        using (values.Subscribe(Console.WriteLine))
        {
            s.Value = 3; // prints 1,3 - expected 2,3
        }
    }
}

class Source
{
    private int _value;
    public int Value
    {
        get { return _value; }
        set
        {
            if (_value == value)
                return;
            _value = value;
            if (ValueChanged != null)
                ValueChanged(this, EventArgs.Empty);
        }
    }

    public event EventHandler ValueChanged;
}
Run Code Online (Sandbox Code Playgroud)

如何创建按预期工作的IObservable?

dtb*_*dtb 6

解决方案是订阅一个BehaviorSubjectobservable,并订阅所有观察者BehaviorSubject.该BehaviorSubject会记得的最后通知,并在通知订阅它的新的观察员.

看看Observable.PublishinitialValue参数的扩展方法.这创建了一个IConnectableObservable内部使用的BehaviorSubject.

var s = new Source { Value = 1 };

var values = Observable.FromEvent(h => s.ValueChanged += h,
                                  h => s.ValueChanged -= h)
                       .Select(e => e.NewValue)
                       .Publish(s.Value);

using (values.Connect())                         // subscribes subject to event
{
    using (values.Subscribe(Console.WriteLine))  // subscribes to subject
    {
        s.Value = 2;
    }                                            // unsubscribes from subject

    using (values.Subscribe(Console.WriteLine))  // subscribes to subject
    {
        s.Value = 3;
    }                                            // unsubscribes from subject

}                                            // unsubscribes subject from event
Run Code Online (Sandbox Code Playgroud)

(另)