/// <summary> /// Creates a delayable signal that can postpone applying updates to a wrapped signal. /// </summary> public static DelayableSignal <T> ToDelayable <T>(this IReadableSignal <T> source) { CheckValue(source, nameof(source)); var result = new DelayableSignal <T>(source.Context); Update(); return(result); void Update() { var snapshot = source.GetSnapshot(); result.Value = snapshot.Value; snapshot.OnChange(Update); } }
/// <summary> /// Flattens a nested signal into a new signal. /// </summary> public static IReadableSignal <T> Flatten <T>(this IReadableSignal <IReadableSignal <T> > source) { CheckValue(source, nameof(source)); var result = new Signal <T>(source.Context); ISignalSnapshot <IReadableSignal <T> > outerSnapshot; IDisposable innerSubscription = null; OnOuterChanged(); return(result); void OnOuterChanged() { // If we were subscribed to a different signal, we don't care about it anymore since the outer changed. innerSubscription?.Dispose(); outerSnapshot = source.GetSnapshot(); if (outerSnapshot.Value is null) { result.Value = default; innerSubscription = null; } else { if (!ReferenceEquals(outerSnapshot.Value.Context, source.Context)) { throw new InvalidOperationException($"Mismatched {nameof(SignalContext)}'s. Cannot mix signals created from different factories."); } var innerSnapshot = outerSnapshot.Value.GetSnapshot(); result.Value = innerSnapshot.Value; innerSubscription = innerSnapshot.OnChange(OnInnerChanged); } outerSnapshot.OnChange(OnOuterChanged); } void OnInnerChanged() { // No need to unsubscribe, the old subscription will never fire again and GC will clean things up eventually var innerSnapshot = outerSnapshot.Value.GetSnapshot(); result.Value = source.Value.Value; innerSubscription = innerSnapshot.OnChange(OnInnerChanged); } }
/// <summary> /// Projects the <paramref name="source"/> signal into a new signal /// using the <paramref name="selector"/> function. /// </summary> public static IReadableSignal <TResult> Select <TSource, TResult>( this IReadableSignal <TSource> source, Func <TSource, TResult> selector) { CheckValue(source, nameof(source)); CheckValue(selector, nameof(selector)); var result = new Signal <TResult>(source.Context); Update(); return(result); void Update() { var snapshot = source.GetSnapshot(); result.Value = selector(snapshot.Value); snapshot.OnChange(Update); } }