如果我有a
和b
的可观察量,并且我想根据第三个可观察量c
在两者之间交替,我将如何去做呢? 弹珠图看起来像这样:
a: -a1---------a2----a3--a4------a5--a6-----a7----
b: -b1--b2----b3--b4----------------b5---b6---b7--
c: --------c-----------c--------c----------c------
alternate
-a1--------b3--b4-----a4---------b5---b6-a7----
这很好用:
var query =
a.Publish(pa =>
b.Publish(pb =>
c.StartWith("c")
.Select((x, n) => n % 2 == 0 ? pa : pb)
.Switch()));
我使用以下代码进行了测试:
var xs = new []
{
"a1", "b1", "b2", "c", "b3",
"a2", "b4", "a3", "c", "a4",
"c", "a5", "b5", "a6", "b6",
"c", "a7", "b7",
}
.ToObservable()
.Publish();
var a = xs.Where(x => x.StartsWith("a"));
var b = xs.Where(x => x.StartsWith("b"));
var c = xs.Where(x => x.StartsWith("c"));
var query = ...
query.Subscribe(Console.WriteLine);
xs.Connect();
我得到了这个结果:
a1
b3
b4
a4
b5
b6
a7
xs.Connect()
只需要使xs
可观察的工作作为生成三个序列的简单方法。
甚至作为扩展方法:
public static IObservable<TSource> Alternate<TSource>(
this IObservable<TSource> leftSelector,
bool startLeft,
IObservable<TSource> left,
IObservable<TSource> right)
{
return
left.Publish(pl =>
right.Publish(pr =>
leftSelector.StartWith(default(TSource))
.Select((x, n) => (n % 2 == (startLeft ? 0 : 1)) ? pl : pr)
.Switch()));
}
这是另一种方式,类似的想法 - 它还允许您指定选择器的起始值。
关键思想是使用 Zip
和 MostRecent
将每个值流与最新的选择器值压缩。然后,我们可以根据选择器值过滤每个压缩流并合并它们。
public static IObservable<TSource> Alternate<TSource>(
this IObservable<bool> leftSelector,
bool startLeft,
IObservable<TSource> left,
IObservable<TSource> right)
{
return leftSelector.Publish(
selector =>
Observable.Merge(
left.Zip(selector.MostRecent(startLeft), Tuple.Create)
.Where(l => l.Item2),
right.Zip(selector.MostRecent(startLeft), Tuple.Create)
.Where(r => !r.Item2)))
.Select(res => res.Item1);
}
此外,这是Enigmativity的简洁方法,经过轻微调整以适应此处的输入:
public static IObservable<TSource> Alternate2<TSource>(
this IObservable<bool> leftSelector,
bool startLeft,
IObservable<TSource> left,
IObservable<TSource> right)
{
return
left.Publish(l =>
right.Publish(r =>
leftSelector.StartWith(startLeft)
.Select(s => s ? l : r)
.Switch()));
}
此解决方案将 c 声明为 Observable<bool>
结果Observable
根据 c 的最新值发出来自 a 和 b 的值。来自 a 和 b 的值被包装到容器类中以供Merge
var a = Observable.Range(1, 10);
var b = Observable.Range(10, 20);
var merged = a.Select(i => new Container {id = "a", value = i})
.Merge( b.Select(i => new Container {id = "b", value = i}));
var c = Observable.Return(true);
var result = merged.CombineLatest( c , (ab, selector )
=> (selector && ab.id == "a") || (!selector && ab.id == "b") ? ab : null)
.Where(i => i != null);
public class Container
{
public string id;
public int value;
}