首先介绍一下IObserver<T>的一点扩展方法。
public static IObserver<T> Safe<T>(this IObserver<T> observer)
{
var done = false;
return Observer.Create<TResult>(
value =>
{
if (!done)
{
observer.OnNext(value);
}
},
error =>
{
if (!done)
{
done = true;
observer.OnError(error);
}
},
() =>
{
if (!done)
{
done = true;
observer.OnCompleted();
}
});
}
这只是确保观察者在模式OnNext*(OnError|OnCompleted) 中被调用,而违反该模式的行为将被忽略。
我们现在可以通过按键缓冲两个序列中的值并仅在两个序列之间的键匹配时发出它们来实现您描述的运算符。
public static IObservable<TResult> Join<T1, T2, TKey, TResult>(
IObservable<T1> source1,
IObservable<T2> source2,
Func<T1, TKey> key1,
Func<T2, TKey> key2,
Func<T1, T2, TResult> selector)
{
return Observable.Create<TResult>(observer =>
{
var dict1 = new Dictionary<TKey, T1>();
var dict2 = new Dictionary<TKey, T2>();
var gate = new object();
var safeObserver = observer.Safe();
Action<TKey> emit = k =>
{
T1 value1;
T2 value2;
if (dict1.TryGetValue(k, out value1) && dict2.TryGetValue(k, out value2))
{
var result = selector(value1, value2);
safeObserver.OnValue(result);
dict1.Remove(k);
dict2.Remove(k);
}
};
return new CompositeDisposable(
source1.Synchronize(gate).Subscribe(
value1 =>
{
var k = key1(value1);
dict1[k] = value1;
emit(k);
},
safeObserver.OnError,
safeObserver.OnCompleted),
source2.Synchronize(gate).Subscribe(
value2 =>
{
var k = key2(value2);
dict2[k] = value2;
emit(k);
},
safeObserver.OnError,
safeObserver.OnCompleted));
});
}
例子:
IObservable<AlertData> alertDatas = ...;
IObservable<SoundRequestData> = soundRequestDatas = ...;
IObservable<AlertDataViewModel> alertDataViewModels = Join(
alertDatas,
soundRequestDatas,
alertData => alertData.Id,
soundRequestData => soundRequestData.Id,
(alertData, soundRequestData) => new AlertDataViewModel
{
AlertData = alertData,
SoundRequestData = soundRequestData
});