Rx Merge + CombineLatest?

时间:2016-07-08 14:09:20

标签: c# system.reactive

CombineLatest在两个观察者都已开始时开始。

A     1----------2---------------
B     -----a----------b---c------
C     -----1a----2a---2b--2c-----   C = A.CombineLatest(B)

当A或B启动时,Merge运算符启动。但是,它无法将A和B的最新值组合在一起。

A     1----------2---------------
B     -----a----------b---c------
C     1----a-----2----b---c------   C = A.Merge(B)

我需要一个类似于Merge的运算符,除了它允许我在两个observable都已启动时将A和B组合成最新值:

A    1----------2---------------
B    -----a----------b---c------
C    1----1a----2a---2b--2c-----   C = A.MergeOrCombineLatest(B)

它的签名可能如下所示:

Observable<C> MergeOrCombineLatest<A, B, C>(
     this IObservable<A> a,
     IObservable<B> b,
     Func<A, C> aResultSelector, // When A starts before B
     Func<B, C> bResultSelector, // When B starts before A
     Func<A, B, C> bothResultSelector) // When both A and B have started

如何实施此运营商?

2 个答案:

答案 0 :(得分:4)

这对我有用:

public static IObservable<C> MergeOrCombineLatest<A, B, C>(
    this IObservable<A> a,
    IObservable<B> b,
    Func<A, C> aResultSelector, // When A starts before B
    Func<B, C> bResultSelector, // When B starts before A
    Func<A, B, C> bothResultSelector) // When both A and B have started
{
    return
        a.Publish(aa =>
            b.Publish(bb =>
                aa.CombineLatest(bb, bothResultSelector).Publish(xs =>
                    aa
                        .Select(aResultSelector)
                        .Merge(bb.Select(bResultSelector))
                        .TakeUntil(xs)
                        .SkipLast(1)
                        .Merge(xs))));
}

然后这个:

var a = new Subject<int>();
var b = new Subject<string>();

var C = a.MergeOrCombineLatest(b, x => $"{x}!!", y => $"{y}!!", (x, y) => $"{x}{y}");

C.Subscribe(x => Console.WriteLine(x));

b.OnNext("x");
b.OnNext("y");
b.OnNext("z");
a.OnNext(1);
a.OnNext(5);
a.OnNext(6);
b.OnNext("a");
a.OnNext(2);
b.OnNext("b");
b.OnNext("c");

......给出了这个:

x!!
y!!
z!!
1z
5z
6z
6a
2a
2b
2c

答案 1 :(得分:1)

首先你选择A,B的特殊值,Observables永远不会发出它,让它为null:

A specialA = null;
B specialB = null;

然后

Observable<C> MergeOrCombineLatest<A, B, C>(
  this IObservable<A> a,
  IObservable<B> b,
  Func<A, C> aResultSelector, // When A starts before B
  Func<B, C> bResultSelector, // When B starts before A
  Func<A, B, C> bothResultSelector) // When both A and B have started
{
  return a.StartWith(specialA).CombineLatest(b.StartWith(specialB), 
    (aval, bval) => {
      if (aval == specialA) return bval == specialB ? default(C) : bResultSelector(bval);
      if (bval == specialB) return aResultSelector(bval);
      return bothResultSelector(aval, bval);
    }
  ).skip(1);  // skip the first emission where both are special values
}