如果我有三个observable,我如何组合它们,这样当第一个发出一个新值时,我可以将它与另外两个发出的最新值结合起来。重要的是我们只产生一个新值的组合当第一个观察者有一个新的值时,观察到其他两个是否已经发出新的值。
感谢。
答案 0 :(得分:3)
您可以执行此操作(请注意,如果源2和3尚未发出,则会获得默认值,并且我允许您选择提供这些默认值):
public static class ObservableExtensions
{
public static IObservable<TResult> CombineFirstWithLatestOfRest
<TSource1, TSource2, TSource3, TResult>(
this IObservable<TSource1> source1,
IObservable<TSource2> source2,
IObservable<TSource3> source3,
Func<TSource1, TSource2, TSource3, TResult> resultSelector,
TSource2 source2Default = default(TSource2),
TSource3 source3Default = default(TSource3))
{
var latestOfRest = source2.CombineLatest(source3, Tuple.Create);
return source1.Zip(latestOfRest.MostRecent(
Tuple.Create(source2Default, source3Default)),
(s1,s23) => resultSelector(s1, s23.Item1, s23.Item2));
}
}
使用示例:
var source1 = Observable.Interval(TimeSpan.FromSeconds(2));
var source2 = Observable.Interval(TimeSpan.FromSeconds(1));
var source3 = Observable.Interval(TimeSpan.FromSeconds(3.5));
var res = source1.CombineFirstWithLatestOfRest(source2, source3,
(x,y,z) => string.Format("1: {0} 2: {1} 3: {2}", x,y,z));
res.Subscribe(Console.WriteLine);
这里有一个微妙的问题,在你的场景中可能很重要。 CombineLatest
的一个有时不受欢迎的方面是它在每个贡献流中都有一个值之前不会发出。这意味着在上面的示例中,较慢的source3
占据source2
并且错过了值。具体而言,返回的事件将具有source2
和source3
的默认值,直到两者每个都发出至少一个事件。使用默认值启动source2
和source3
是此行为的一种方便的解决方法,我们可以使用它,因为source1
驱动事件:
public static class ObservableExtensions
{
public static IObservable<TResult> CombineFirstWithLatestOfRest
<TSource1, TSource2, TSource3, TResult>(
this IObservable<TSource1> source1,
IObservable<TSource2> source2,
IObservable<TSource3> source3,
Func<TSource1, TSource2, TSource3, TResult> resultSelector,
TSource2 source2Default = default(TSource2),
TSource3 source3Default = default(TSource3))
{
source2 = source2.StartWith(source2Default); // added this
source3 = source3.StartWith(source3Default); // and this
var lastestOfRest = source2.CombineLatest(source3, Tuple.Create);
return source1.Zip(lastestOfRest.MostRecent(
Tuple.Create(source2Default, source3Default)), // now redundant
(s1,s23) => resultSelector(s1, s23.Item1, s23.Item2));
}
}