Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/270.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 观察其他观察者未观察到的值_C#_System.reactive - Fatal编程技术网

C# 观察其他观察者未观察到的值

C# 观察其他观察者未观察到的值,c#,system.reactive,C#,System.reactive,我有一个能发出独特值的可观察对象,例如 var source=Observable.Range(1100.Publish(); source.Connect(); 我希望从两个观察者处观察其值,但每个观察者仅收到其他观察者未看到的值的通知 因此,如果第一个观察者包含值10,则第二个观察者永远不会收到10值的通知 更新 我选择了阿斯蒂的答案,因为这是第一个答案,尽管它的方向是正确的,但还是投票支持了什洛莫的答案。太糟糕了,我不能同时接受这两个答案,因为@Shlomo answer更为正确,我真的

我有一个能发出独特值的可观察对象,例如

var source=Observable.Range(1100.Publish();
source.Connect();
我希望从两个观察者处观察其值,但每个观察者仅收到其他观察者未看到的值的通知

因此,如果第一个观察者包含值10,则第二个观察者永远不会收到10值的通知

更新


我选择了阿斯蒂的答案,因为这是第一个答案,尽管它的方向是正确的,但还是投票支持了什洛莫的答案。太糟糕了,我不能同时接受这两个答案,因为@Shlomo answer更为正确,我真的很感谢他在这个标签上的帮助。

对于不同的观察者,观察者的行为不应该有所不同;更好的方法是为每个观察者提供自己的过滤观察值

这就是说,如果您的约束要求您在单个可观察对象中需要此行为,那么我们可以使用循环方法

    public static IEnumerable<T> Repeat<T>(this IEnumerable<T> source)
    {
        for (; ; )
            foreach (var item in source.ToArray())
                yield return item;
    }

    public static IObservable<T> RoundRobin<T>(this IObservable<T> source)
    {
        var subscribers = new List<IObserver<T>>();
        var shared = source
            .Zip(subscribers.Repeat(), (value, observer) => (value, observer))
            .Publish()
            .RefCount();

        return Observable.Create<T>(observer =>
        {
            subscribers.Add(observer);
            var subscription = 
                shared
                .Where(pair => pair.observer == observer)
                .Select(pair => pair.value)
                .Subscribe(observer);

            var dispose = Disposable.Create(() => subscribers.Remove(observer));
            return new CompositeDisposable(subscription, dispose);
        });
    }
结果:

One sees 1
Two sees 2
One sees 3
Two sees 4
One sees 5
Two sees 6
One sees 7
Two sees 8
One sees 9
Two sees 10

如果你已经有了一个观察者列表,代码就会简单得多。

编辑:@Asti修复了他的错误,我根据他的答案修复了我的错误。现在,我们的答案基本相同。我有一个想法,如何做一个纯粹的反应,如果我有时间,我会张贴后

固定代码:

public static IObservable<T> RoundRobin2<T>(this IObservable<T> source)
{
    var subscribers = new BehaviorSubject<ImmutableList<IObserver<T>>>(ImmutableList<IObserver<T>>.Empty);
    ImmutableList<IObserver<T>> latest = ImmutableList<IObserver<T>>.Empty;
    subscribers.Subscribe(l => latest = l);

    var shared = source
            .Select((v, i) => (v, i))
            .WithLatestFrom(subscribers, (t, s) => (t.v, t.i, s))
            .Publish()
            .RefCount();
    return Observable.Create<T>(observer =>
    {
        subscribers.OnNext(latest.Add(observer));
        var dispose = Disposable.Create(() => subscribers.OnNext(latest.Remove(observer)));

        var sub = shared
            .Where(t => t.i % t.s.Count == t.s.FindIndex(o => o == observer))
            .Select(t => t.v)
            .Subscribe(observer);

        return new CompositeDisposable(dispose, sub);
    });
}
这并不是:

var source = Observable.Range(1, 20).Publish();
var dist = source.RoundRobin();
dist.Take(1).Subscribe(i => Console.WriteLine($"One sees {i}"));
dist.Subscribe(i => Console.WriteLine($"Two sees {i}"));
输出为:

One sees 1
Two sees 1
Two sees 2
Two sees 3
Two sees 4
...
我起初以为是虫子,但现在我不确定。
Repeat
中的
.ToArray()
应该会处理这个问题。我还编写了一个纯可观察的实现,它有相同的bug。此实现不能保证完美的循环,但这不是问题:

public static IObservable<T> RoundRobin2<T>(this IObservable<T> source)
{
    var subscribers = new BehaviorSubject<ImmutableList<IObserver<T>>>(ImmutableList<IObserver<T>>.Empty);
    ImmutableList<IObserver<T>> latest = ImmutableList<IObserver<T>>.Empty;
    subscribers.Subscribe(l => latest = l);

    var shared = source
            .Select((v, i) => (v, i))
            .WithLatestFrom(subscribers, (t, s) => (t.v, t.i, s))
            .Publish()
            .RefCount();
    return Observable.Create<T>(observer =>
    {
        subscribers.OnNext(latest.Add(observer));
        var dispose = Disposable.Create(() => subscribers.OnNext(latest.Remove(observer)));

        var sub = shared
            .Where(t => t.i % t.s.Count == t.s.FindIndex(o => o == observer))
            .Select(t => t.v)
            .Subscribe(observer);

        return new CompositeDisposable(dispose, sub);
    });
}
公共静态IObservable RoundRobin2(此IObservable源)
{
var subscribers=newbehaviorsubject(ImmutableList.Empty);
ImmutableList latest=ImmutableList.Empty;
订阅(l=>latest=l);
var共享=源
.选择((v,i)=>(v,i))
.最晚从(订户,(t,s)=>(t.v,t.i,s))
.Publish()
.RefCount();
返回可观察的。创建(观察者=>
{
subscribers.OnNext(latest.Add(observer));
var dispose=Disposable.Create(()=>subscribers.OnNext(latest.Remove(observer));
var sub=共享
其中(t=>t.i%t.s.Count==t.s.FindIndex(o=>o==observer))
.选择(t=>t.v)
.签署(观察员);
返回新的CompositeDisposable(dispose,sub);
});
}

这是一个使用TPL数据流的简单分布式队列实现。但对于不同的观察者看不到相同的价值观,其行为不正确的可能性很小。这不是循环赛,但实际上有背压语义

    public static IObservable<T> Distribute<T>(this IObservable<T> source)
    {
        var buffer = new BufferBlock<T>();
        source.Subscribe(buffer.AsObserver());             
        return Observable.Create<T>(observer =>
            buffer.LinkTo(new ActionBlock<T>(observer.OnNext, new ExecutionDataflowBlockOptions { BoundedCapacity = 1 })
        );
    }

我可能更喜欢完全跳过Rx,只使用TPL数据流。

回答得好!请注意:
Repeat
扩展方法包含在包中。谢谢!我先写了这个,然后意识到他可能会说没有叫做
Repeat
的方法,所以我写了这个。以前发生过:DAlso,
EnumerableEx
中的
Repeat
的行为略有不同。我使用
ToArray()
强制eval,否则列表可能会出现“集合在枚举时被修改”错误。是的,如果在
source.Connect()之后添加了另一个订阅服务器,这将是一个问题。或者,您可以将
ToArray
Repeat
subscribers.ToArray().Repeat()
。谢谢Theodor!我也从你的答案中学到了很多东西!:)伟大的答案是@Shlomo和@Asti,关于
完美循环
的一个小小的澄清是最受欢迎的,因为循环技术依赖于一个简单的mod操作,用户数量可以随时改变,不能保证观察者不会错过机会。最终没有那么大的问题。一个项目不会被遗漏,但是一个观察者可能会被跳过,根据你的问题,这不是一个大问题。我认为这可能是通过Rx中的某种窗口来实现的,但我可以看到性能很差。我认为最好的表现方式是在TPL数据流中,而不是Rx。如果你有多余的投票权,你也可以对这个问题进行投票。这是一个好主意!:-)在
Observable.Range(1100)
上调用
.Publish()
不是一个好主意,否则无论以后有多少订阅者,它都只运行一次。谢谢,我只是想表达一个热的Observable,而不是一个在每个订阅上发出所有信息的函数。我想更好的办法是在发射之间增加一些延迟。
public static IObservable<T> RoundRobin2<T>(this IObservable<T> source)
{
    var subscribers = new BehaviorSubject<ImmutableList<IObserver<T>>>(ImmutableList<IObserver<T>>.Empty);
    ImmutableList<IObserver<T>> latest = ImmutableList<IObserver<T>>.Empty;
    subscribers.Subscribe(l => latest = l);

    var shared = source
            .Select((v, i) => (v, i))
            .WithLatestFrom(subscribers, (t, s) => (t.v, t.i, s))
            .Publish()
            .RefCount();
    return Observable.Create<T>(observer =>
    {
        subscribers.OnNext(latest.Add(observer));
        var dispose = Disposable.Create(() => subscribers.OnNext(latest.Remove(observer)));

        var sub = shared
            .Where(t => t.i % t.s.Count == t.s.FindIndex(o => o == observer))
            .Select(t => t.v)
            .Subscribe(observer);

        return new CompositeDisposable(dispose, sub);
    });
}
    public static IObservable<T> Distribute<T>(this IObservable<T> source)
    {
        var buffer = new BufferBlock<T>();
        source.Subscribe(buffer.AsObserver());             
        return Observable.Create<T>(observer =>
            buffer.LinkTo(new ActionBlock<T>(observer.OnNext, new ExecutionDataflowBlockOptions { BoundedCapacity = 1 })
        );
    }
One sees 1
Two sees 2
One sees 3
Two sees 4
One sees 5
One sees 6
One sees 7
One sees 8
One sees 9
One sees 10