/// <summary> /// Creates an observable with two pairwise notification channels by combining the latest values of the specified /// observable sequences and choosing which channels will receive values based on the specified function. /// </summary> /// <typeparam name="TLeft">Type of the left notification channel.</typeparam> /// <typeparam name="TRight">Type of the right notification channel.</typeparam> /// <param name="leftSource">The observable sequence that provides notifications for the left channel.</param> /// <param name="rightSource">The observable sequence that provides notifications for the right channel.</param> /// <param name="directionSelector">Selects the channels that will receive notifications for every pair.</param> /// <returns>A new observable sequence containing the latest values in the specified observable sequences.</returns> public static IObservable <Either <TLeft, TRight> > Pair <TLeft, TRight>( this IObservable <TLeft> leftSource, IObservable <TRight> rightSource, Func <TLeft, TRight, PairDirection> directionSelector) { Contract.Requires(leftSource != null); Contract.Requires(rightSource != null); Contract.Requires(directionSelector != null); Contract.Ensures(Contract.Result <IObservable <Either <TLeft, TRight> > >() != null); return(EitherObservable.Create <TLeft, TRight>( observer => leftSource.Always().StartWith(Maybe.Empty <TLeft>()) .CombineLatest( rightSource.Always().StartWith(Maybe.Empty <TRight>()), (left, right) => new { left, right }) .SubscribeSafe( pair => { if (!pair.left.HasValue) { if (pair.right.HasValue) { observer.OnNextRight(pair.right.Value); } } else if (!pair.right.HasValue) { observer.OnNextLeft(pair.left.Value); } else { var left = pair.left.Value; var right = pair.right.Value; switch (directionSelector(left, right)) { case PairDirection.Left: observer.OnNextLeft(left); break; case PairDirection.Right: observer.OnNextRight(right); break; case PairDirection.Both: observer.OnNextLeft(left); observer.OnNextRight(right); break; case PairDirection.Neither: break; default: throw new InvalidOperationException(Errors.InvalidPairDirectionValue); } } }, observer.OnError, observer.OnCompleted))); }