Source/Tx.Core/Converter.cs (82 lines of code) (raw):

namespace Tx.Core { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; public class Converter<TIn, TOut> : IObserver<TIn> { private readonly IDictionary<Type, Func<TIn, TOut>> serializers = new Dictionary<Type, Func<TIn, TOut>>(); private readonly IObserver<TOut> next; private ITransformBuilder<TOut>[] transformBuilders; public Converter(IObserver<TOut> next, params ITransformBuilder<TOut>[] transformBuilders) { if (next == null) { throw new ArgumentNullException(nameof(next)); } if (transformBuilders == null) { throw new ArgumentNullException(nameof(transformBuilders)); } this.next = next; this.transformBuilders = transformBuilders; } /// <summary> /// Provides the observer with new data. /// </summary> /// <param name="value">The current notification information.</param> public void OnNext(TIn value) { var type = value.GetType(); Func<TIn, TOut> transform; if (!this.serializers.TryGetValue(type, out transform)) { transform = this.BuildTransform(type); this.serializers.Add(type, transform); } if (transform != null) { var envelope = transform(value); this.next.OnNext(envelope); } } /// <summary> /// Notifies the observer that the provider has experienced an error condition. /// </summary> /// <param name="error">An object that provides additional information about the error.</param> public void OnError(Exception error) { this.next.OnError(error); } /// <summary> /// Notifies the observer that the provider has finished sending push-based notifications. /// </summary> public void OnCompleted() { this.next.OnCompleted(); } public void RegisterTransformerBuilder(ITransformBuilder<TOut> transformBuilder) { if (transformBuilder == null) { throw new ArgumentNullException(nameof(transformBuilder), "Cannot be null."); } this.transformBuilders = this.transformBuilders .Concat(new[] { transformBuilder }) .ToArray(); } private Func<TIn, TOut> BuildTransform(Type type) { foreach (var transformBuilder in this.transformBuilders) { var transform = transformBuilder.Build<TIn, TOut>(type); if (transform != null) { return transform; } } return null; } } internal static class E { public static Func<TIn, TOut> Build<TIn, TOut>(this ITransformBuilder<TOut> transformBuilder, Type type) { var method = typeof(ITransformBuilder<TOut>) .GetRuntimeMethod("Build", new Type[0]) .MakeGenericMethod(type) .Invoke(transformBuilder, new object[0]) as Func<TIn, TOut>; return method; } } }