C# &引用;“合并”;一个流,用于生成每个流的最新值流

C# &引用;“合并”;一个流,用于生成每个流的最新值流,c#,system.reactive,C#,System.reactive,我有一个IObservable,其中每个内部IObservable都是一个值流,后跟一个最终的OnCompleted事件 我想将其转换为一个IObservable,一个由未完成的任何内部流的最新值组成的流。每当从一个内部流(或内部流过期)生成新值时,它应该生成一个新的IEnumerable 最容易用大理石图显示(我希望它足够全面): ([]是空的IEnumerable,-|表示未完成的) 您可以看到,它稍微类似于combinelateest操作。 我一直在玩弄Join和GroupJoin,但毫无

我有一个
IObservable
,其中每个内部
IObservable
都是一个值流,后跟一个最终的
OnCompleted
事件

我想将其转换为一个
IObservable
,一个由未完成的任何内部流的最新值组成的流。每当从一个内部流(或内部流过期)生成新值时,它应该生成一个新的
IEnumerable

最容易用大理石图显示(我希望它足够全面):

[]
是空的
IEnumerable
-|
表示未完成的)

您可以看到,它稍微类似于
combinelateest
操作。 我一直在玩弄
Join
GroupJoin
,但毫无用处,但我觉得这几乎肯定是正确的方向

我希望在此操作符中使用尽可能少的状态

更新
我已经更新了这个问题,使其不仅包括单值序列-如果序列未产生值,则结果
IObservable
应仅包括每个序列的最新值,不应包括它。

此解决方案适用于一个项目流,但不幸的是,它会将每个项目累积到一个内部流中,直到完成为止

public static IObservable<IEnumerable<T>> MergeLatest<T>(this IObservable<IObservable<T>> source)
{
    return Observable.Create<IEnumerable<T>>(obs =>
    {
        var collection = new PerishableCollection<T>();
        return source.Subscribe(duration =>
        {
            var lifetime = new DisposableLifetime(); // essentially a CancellationToken
            duration
                .Subscribe(
                    x => // on initial item
                    {
                        collection.Add(x, lifetime.Lifetime);
                        obs.OnNext(collection.CurrentItems().Select(p => p.Value));
                    },
                    () => // on complete
                    {
                        lifetime.Dispose(); // removes the item
                        obs.OnNext(collection.CurrentItems().Select(p => p.Value));
                    }
            );
        });
    });
}
public static IObservable MergeLatest(此IObservable源)
{
返回可观察的。创建(obs=>
{
var collection=新易腐集合();
返回源。订阅(持续时间=>
{
var lifety=new disposablelifety();//本质上是一个CancellationToken
期间
.订阅(
x=>//在初始项上
{
集合。添加(x,生存期。生存期);
obs.OnNext(collection.CurrentItems().Select(p=>p.Value));
},
()=>//完成时
{
lifetime.Dispose();//删除该项
obs.OnNext(collection.CurrentItems().Select(p=>p.Value));
}
);
});
});
}

这是昨天基于您的解决方案的版本,针对新的需求进行了调整。基本思想是将引用放入易腐集合,然后在内部序列生成新值时更新引用的值

我还对其进行了修改,以正确跟踪内部订阅,并在外部可观察对象被取消订阅时取消订阅

还修改为在任何流产生错误时将其全部删除

最后,我修复了一些可能违反Rx指南的比赛条件。如果您的内部观察对象同时从不同线程触发,您可能会同时调用
obs.OnNext
,这是一个很大的禁忌。因此,我使用相同的锁对每个内部观察对象进行了选通,以防止出现这种情况(请参阅
同步
调用)。请注意,正因为如此,您可能不需要使用常规的双链接列表而不是
易逝性集合
,因为现在使用集合的所有代码都在锁中,因此您不需要
易逝性集合
的线程保证

// Acts as a reference to the current value stored in the list
private class BoxedValue<T>
{
    public T Value;
    public BoxedValue(T initialValue) { Value = initialValue; }
}

public static IObservable<IEnumerable<T>> MergeLatest<T>(this IObservable<IObservable<T>> source)
{
    return Observable.Create<IEnumerable<T>>(obs =>
    {
        var collection = new PerishableCollection<BoxedValue<T>>();
        var outerSubscription = new SingleAssignmentDisposable();
        var subscriptions = new CompositeDisposable(outerSubscription);
        var innerLock = new object();

        outerSubscription.Disposable = source.Subscribe(duration =>
        {
            BoxedValue<T> value = null;
            var lifetime = new DisposableLifetime(); // essentially a CancellationToken
            var subscription = new SingleAssignmentDisposable();

            subscriptions.Add(subscription);
            subscription.Disposable = duration.Synchronize(innerLock)
                .Subscribe(
                    x =>
                    {
                        if (value == null)
                        {
                            value = new BoxedValue<T>(x);
                            collection.Add(value, lifetime.Lifetime);
                        }
                        else
                        {
                            value.Value = x;
                        }
                        obs.OnNext(collection.CurrentItems().Select(p => p.Value.Value));
                    },
                    obs.OnError, // handle an error in the stream.
                    () => // on complete
                    {
                        if (value != null)
                        {
                            lifetime.Dispose(); // removes the item
                            obs.OnNext(collection.CurrentItems().Select(p => p.Value.Value));
                            subscriptions.Remove(subscription); // remove this subscription
                        }
                    }
            );
        });

        return subscriptions;
    });
}
//用作对列表中存储的当前值的引用
私有类BoxedValue
{
公共价值观;
public BoxedValue(T initialValue){Value=initialValue;}
}
公共静态IObservable MergeLatest(此IObservable源)
{
返回可观察的。创建(obs=>
{
var collection=新易腐集合();
var outerSubscription=new SingleAssignmentDisposable();
var订阅=新的CompositeDisposable(外部订阅);
var innerLock=新对象();
outerSubscription.Disposable=source.Subscribe(持续时间=>
{
BoxedValue值=空;
var lifety=new disposablelifety();//本质上是一个CancellationToken
var subscription=new SingleAssignmentDisposable();
订阅。添加(订阅);
subscription.Disposable=duration.Synchronize(innerLock)
.订阅(
x=>
{
如果(值==null)
{
值=新框的值(x);
集合。添加(值、生存期、生存期);
}
其他的
{
数值。数值=x;
}
obs.OnNext(collection.CurrentItems().Select(p=>p.Value.Value));
},
obs.OnError,//处理流中的错误。
()=>//完成时
{
if(值!=null)
{
lifetime.Dispose();//删除该项
obs.OnNext(collection.CurrentItems().Select(p=>p.Value.Value));
订阅。删除(订阅);//删除此订阅
}
}
);
});
退还订阅费;
});
}
,的创建者-它使用的是与Brandon的解决方案非常相似的实现:

public static IObservable<IEnumerable<T>> CombineLatestEagerly<T>(this IObservable<IObservable<T>> source)
{
  return source
    // Reify completion to force an additional combination:
    .Select(o => o.Select(v => new { Value = v, HasValue = true })
                  .Concat(Observable.Return(new { Value = default(T), HasValue = false })))
    // Merge a completed observable to force combination with the first real inner observable:
    .Merge(Observable.Return(Observable.Return(new { Value = default(T), HasValue = false })))
    .CombineLatest()
    // Filter out completion notifications:
    .Select(l => l.Where(v => v.HasValue).Select(v => v.Value));
}
公共静态IObservable组合(此IObservable源)
{
返回源
//具体化完成以强制进行其他组合:
.Select(o=>o.Select(v=>new{Value=v,HasValue=true})
.Concat(Observable.Return(new{Value=default(T),HasValue=false}))
//合并一个完整的可观察到的强制组合与第一个真实的内部可观察到的:
.合并(可观察)返回(可观察
public static IObservable<IEnumerable<T>> CombineLatestEagerly<T>(this IObservable<IObservable<T>> source)
{
  return source
    // Reify completion to force an additional combination:
    .Select(o => o.Select(v => new { Value = v, HasValue = true })
                  .Concat(Observable.Return(new { Value = default(T), HasValue = false })))
    // Merge a completed observable to force combination with the first real inner observable:
    .Merge(Observable.Return(Observable.Return(new { Value = default(T), HasValue = false })))
    .CombineLatest()
    // Filter out completion notifications:
    .Select(l => l.Where(v => v.HasValue).Select(v => v.Value));
}