Func<string, int> match = new Matcher<string, int> { {s => string.IsNullOrEmpty(s), s => 0}, {s => true, s => s.Length} }; int len1 = match(null); // 0 int len2 = match("abc"); // 3
public class ExprMatcher<TIn, TOut> : IEnumerable<Pair<Expression<Predicate<TIn>>, Expression<Func<TIn, TOut>>>> { private Func<TIn, TOut> _matcher; private Func<TIn, TOut> Matcher { get { return _matcher ?? (_matcher = CompileMatcher()); } } private readonly List<Pair<Expression<Predicate<TIn>>, Expression<Func<TIn, TOut>>>> _caseList = new List<Pair<Expression<Predicate<TIn>>, Expression<Func<TIn, TOut>>>>(); public void Add(Expression<Predicate<TIn>> predicate, Expression<Func<TIn, TOut>> function) { _caseList.Add(new Pair<Expression<Predicate<TIn>>, Expression<Func<TIn, TOut>>>(predicate, function)); } private Func<TIn, TOut> CompileMatcher() { var reverted = Enumerable.Reverse(_caseList).ToList(); var arg = Expression.Parameter(typeof(TIn)); var retVal = Expression.Label(typeof(TOut)); var matcher = Expression.Block( Expression.Throw(Expression.Constant(new MatchException("Provided value was not matched with any case"))), Expression.Label(retVal, Expression.Constant(default(TOut))) ); foreach (var pair in reverted) { retVal = Expression.Label(typeof(TOut)); var condition = Expression.Invoke(pair.First, arg); var action = Expression.Return(retVal, Expression.Invoke(pair.Second, arg)); matcher = Expression.Block( Expression.IfThenElse(condition, action, Expression.Return(retVal, matcher)), Expression.Label(retVal, Expression.Constant(default(TOut))) ); } return Expression.Lambda<Func<TIn, TOut>>(matcher, arg).Compile(); } public TOut Match(TIn value) { return Matcher(value); } public static implicit operator Func<TIn, TOut>(ExprMatcher<TIn, TOut> matcher) { return matcher.Match; } public IEnumerator<Pair<Expression<Predicate<TIn>>, Expression<Func<TIn, TOut>>>> GetEnumerator() { return _caseList.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }
public sealed class Union<T1, T2> { public object Value { get; private set; } public T1 Value1 { get; private set; } public T2 Value2 { get; private set; } public Union(T1 value) { Value1 = value; Value = value; } public Union(T2 value) { Value2 = value; Value = value; } public static explicit operator T1(Union<T1, T2> value) { return value.Value1; } public static explicit operator T2(Union<T1, T2> value) { return value.Value2; } public static implicit operator Union<T1, T2>(T1 value) { return new Union<T1, T2>(value); } public static implicit operator Union<T1, T2>(T2 value) { return new Union<T1, T2>(value); } }
public class ExprMatcher<T1, T2, T3> : ExprMatcher<T1, Union<T2, T3>> {}
Source: https://habr.com/ru/post/222979/