Last active
August 29, 2015 14:26
-
-
Save isobit/cb74f987442ac0a3e9eb to your computer and use it in GitHub Desktop.
C# Pattern Matching
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Linq; | |
namespace Util | |
{ | |
public static class PatternMatcherConversions | |
{ | |
public static PatternMatcher<T, TRes> Match<T, TRes>(this T o) | |
{ | |
return new PatternMatcher<T, TRes>(o); | |
} | |
public static OrMatchable Or(this object o, object other) | |
{ | |
return new OrMatchable(new List<IMatchable> | |
{ | |
new EqualsMatchable(o), | |
new EqualsMatchable(other) | |
}); | |
} | |
public static EqualsMatchable ToMatchable(this object o) | |
{ | |
return new EqualsMatchable(o); | |
} | |
} | |
public interface IMatchable | |
{ | |
bool IsMatch(object value); | |
} | |
public class EqualsMatchable : IMatchable | |
{ | |
private object _o; | |
public EqualsMatchable(object o) | |
{ | |
_o = o; | |
} | |
public bool IsMatch(object value) | |
{ | |
return _o.Equals(value); | |
} | |
public OrMatchable Or(IMatchable left) | |
{ | |
return new OrMatchable(new List<IMatchable> { this, left }); | |
} | |
} | |
public class OrMatchable : IMatchable | |
{ | |
protected readonly List<IMatchable> Matchables; | |
public OrMatchable(List<IMatchable> matchables) | |
{ | |
Matchables = matchables; | |
} | |
public bool IsMatch(object value) | |
{ | |
return Matchables.Any(m => m.IsMatch(value)); | |
} | |
public OrMatchable Or(IMatchable left) | |
{ | |
return new OrMatchable(Matchables.Concat(new List<IMatchable> { left } ).ToList()); | |
} | |
} | |
public class MatchNotFoundException : Exception | |
{ | |
public MatchNotFoundException(string msg) : base(msg) { } | |
} | |
public class PatternMatcher<T, TResult> | |
{ | |
private readonly T _value; | |
private readonly List<Tuple<Predicate<T>, Func<T, TResult>>> _cases; | |
private Func<T, TResult> _elseFunc; | |
public PatternMatcher(T value) | |
{ | |
_value = value; | |
_cases = new List<Tuple<Predicate<T>, Func<T, TResult>>>(); | |
} | |
public PatternMatcher<T, TResult> Case(Predicate<T> cond, Func<T, TResult> res) | |
{ | |
_cases.Add(Tuple.Create(cond, res)); | |
return this; | |
} | |
public PatternMatcher<T, TResult> Case<TCase>(Func<TCase, TResult> res) where TCase : class | |
{ | |
_cases.Add(Tuple.Create<Predicate<T>, Func<T, TResult>>( | |
v => v is TCase, | |
v => res(v as TCase) | |
)); | |
return this; | |
} | |
public PatternMatcher<T, TResult> Case(T condLit, Func<T, TResult> res) | |
{ | |
_cases.Add(Tuple.Create(new Predicate<T>(v => v.Equals(condLit)), res)); | |
return this; | |
} | |
public PatternMatcher<T, TResult> Case(IMatchable matchable, Func<T, TResult> res) | |
{ | |
_cases.Add(Tuple.Create(new Predicate<T>(v => matchable.IsMatch(v)), res)); | |
return this; | |
} | |
public PatternMatcher<T, TResult> Else(Func<T, TResult> f) | |
{ | |
_elseFunc = f; | |
return this; | |
} | |
public TResult Apply() | |
{ | |
foreach (var item in _cases.Where(item => item.Item1(_value))) | |
{ | |
return item.Item2(_value); | |
} | |
if (_elseFunc != null) | |
{ | |
return _elseFunc(_value); | |
} | |
throw new MatchNotFoundException(string.Format("no match for {0}", _value)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment