Skip to content

Instantly share code, notes, and snippets.

@Novakov
Created December 20, 2013 14:17
Show Gist options
  • Save Novakov/8055374 to your computer and use it in GitHub Desktop.
Save Novakov/8055374 to your computer and use it in GitHub Desktop.
C# pipeline written using | operator - like command line
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace PipelineOperators
{
class Program
{
static void Main(string[] args)
{
var toLower = new LambdaPipelineItem<string, string>(x => x.ToLower());
var replace = new LambdaPipelineItem<string, string>(x => x.Replace("A", "AA"));
var length = new LambdaPipelineItem<string, int>(x => x.Length);
var input = "ABCD";
var output = input | replace | toLower | length;
Console.WriteLine(output.Value);
var o = Pipeline<string>.Of(x => x | replace | toLower | length);
Console.WriteLine(o.Run("ABCD"));
Console.WriteLine(o.Run("1234567"));
}
}
public class Carried<T>
{
private readonly T value;
public T Value { get { return this.value; } }
public Carried(T value)
{
this.value = value;
}
public static implicit operator Carried<T>(T value)
{
return new Carried<T>(value);
}
}
public abstract class PipelineItem<TInput, TOutput> :IPipelineItem
{
public abstract TOutput Run(TInput input);
public static Carried<TOutput> operator |(Carried<TInput> input, PipelineItem<TInput, TOutput> pipelineItem)
{
return pipelineItem.Run(input.Value);
}
object IPipelineItem.Run(object input)
{
return this.Run((TInput) input);
}
}
internal interface IPipelineItem
{
object Run(object input);
}
public class LambdaPipelineItem<TInput, TOutput> : PipelineItem<TInput, TOutput>
{
private readonly Func<TInput, TOutput> action;
public LambdaPipelineItem(Func<TInput, TOutput> action)
{
this.action = action;
}
public override TOutput Run(TInput input)
{
return this.action(input);
}
}
public static class Pipeline<TInput>
{
public static Pipeline<TInput, TOutput> Of<TOutput>(Expression<Func<Carried<TInput>, Carried<TOutput>>> pipeline)
{
var decomposed = DecomposeOr((BinaryExpression) pipeline.Body).Skip(1);
var items = decomposed.Select(ExtractItem).ToList();
return new Pipeline<TInput, TOutput>(items);
}
private static IPipelineItem ExtractItem(Expression arg)
{
return Expression.Lambda<Func<IPipelineItem>>(arg).Compile().Invoke();
}
private static IEnumerable<Expression> DecomposeOr(BinaryExpression expression)
{
if (expression.Left.NodeType == ExpressionType.Or)
{
foreach (var right in DecomposeOr((BinaryExpression)expression.Left))
{
yield return right;
}
}
else
{
yield return expression.Left;
}
yield return expression.Right;
}
}
public class Pipeline<TInput, TOutput>
{
private readonly List<IPipelineItem> items;
internal Pipeline(List<IPipelineItem> items)
{
this.items = items;
}
public TOutput Run(TInput input)
{
return (TOutput) this.items.Aggregate((object)input, (acc, item) => item.Run(acc));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment