收到Elevate(http://elevate.codeplex.com)项目里Pattern Match的启发,对它的实现进行了一些改进,于是写出了这样一段神一般的代码来~
不知道是不是有人能读懂段代码~
代码里把C#的Generic, Extension Method, Lambda Expression, Explicit Implemented Interface等Feature用到了极致~特别是Generic!
太神奇了~哈哈~
为了好理解,先放上一些Elevate 里关于 Pattern Match 的 Sample Code:
/// <summary> /// Pattern matching is used in functional languages. It can be thought of as a "switch statement on steriods"./// Here's an example of very basic pattern matching using Elevate. /// </summary>[Fact]public void PatternMatchingWithFunctions() { //given a value var value = "alpha"; var result = value.Match() //we can start a pattern match like this .With(string.IsNullOrEmpty, stringValue => "empty") //causes the pattern match to return "empty" if value is null or empty .With(stringValue => value.Contains("a"), stringValue => "contains a!") //match any string containing "a" .EndMatch(); Assert.Equal("contains a!", result); } [Fact]public void PatternMatchingWithMatchPatternsAsValues(){ //given a value var value = "gamma"; //we can equality match against values without having to specify //a condition function var result = value.Match() .With("alpha", stringValue => "1st letter = " + stringValue) .With("beta", stringValue => "2nd letter = " + stringValue) .With("gamma", stringValue => "3rd letter = " + stringValue) .EndMatch(); Assert.Equal("3rd letter = gamma", result); } [Fact]public void PatternMatchingWithMatchPatternsAndResultsAsValues(){ //given a value var value = "gamma"; //we can also match without having to speficy a lambda for either the //match predicate or the result var result = value.Match() .With("alpha", "first letter") .With("beta", "second letter") .With("gamma", "third letter") .EndMatch(); Assert.Equal("third letter", result); } [Fact]public void IncompletePatternMatching(){ //given a value var stringValue = "alpha, beta"; //if we construct a pattern match that does not match the value Assert.ThrowsDelegateWithReturn incompleteMatch = () => stringValue.Match() .With(value => value == "alpha", value => "Found foo") .With(value => value == "beta", value => "Found bar") .EndMatch(); //evaluating it will throw Assert.Throws<IncompletePatternMatchException>(incompleteMatch); } [Fact]public void PatternMatchingWithElse(){ //given a value var stringValue = "alpha, beta"; //we can add Else to cause the incomplete match from above to have a //default case var elseMatch = stringValue.Match() .With(value => value == "alpha", value => "Found foo") .With(value => value == "beta", value => "Found bar") .Else(value => "default!") .EndMatch(); Assert.Equal("default!", elseMatch); } [Fact]public void PatternMatchingElseWithValues(){ //given a value var stringValue = "alpha, beta"; //we can specify else as a concrete value without having //to use the input value var elseMatch = stringValue.Match() .With(value => value == "alpha", value => "Found foo") .With(value => value == "beta", value => "Found bar") .Else("default!") .EndMatch(); Assert.Equal("default!", elseMatch); }
Elevate原来的代码仅仅支持用一次,没有办法把这个Pattern给持久化住,然后apply给一系列的值~
这是个比较致命的缺陷,于是,我开始研究它的代码~
Elevate中关于Pattern Match比较关键的几个类是
/// <summary>/// A class containing extensions methods to enable pattern matching. /// </summary>public static class ObjectExtensionsForPatternMatching { /// <summary> /// Begins a pattern match on the specified value /// </summary> /// <typeparam name="T">The type of the value being matched.</typeparam> /// <param name="value">The value to match.</param> /// <returns>The matching context</returns> public static PatternMatchContext<T> Match<T>(this T value) { return new PatternMatchContext<T>(value); }}
/// <summary>
/// A partially complete pattern match with only the input type specified./// </summary> /// <typeparam name="T">The type returned by this pattern match</typeparam> public class PatternMatchContext<T> { internal readonly T value; internal PatternMatchContext(T value) { this.value = value; } /// <summary> /// Specifies the first condition to match against. Also defines the type of the result value. /// </summary> /// <typeparam name="TResult">The type of the result.</typeparam> /// <param name="condition">The condition to match against.</param> /// <param name="result">The result to return if this condition matches.</param> /// <returns>The pattern match with the selector defined</returns> public PatternMatch<T, TResult> With<TResult>( Func<T, bool> condition, Func<T, TResult> result) { if (condition == null) throw new ArgumentNullException("condition", "condition is null."); if (result == null) throw new ArgumentNullException("result", "result is null."); var match = new PatternMatch<T, TResult>(value); return match.With(condition, result); }}
/// <summary> /// A pattern matching block with the return value and the/// the type to match on specified. /// </summary>/// <typeparam name="T">The type of the value being matched.</typeparam> /// <typeparam name="TResult">The type of the result.</typeparam> public class PatternMatch<T, TResult> { private readonly T value; private readonly List<Tuple<Func<T, bool>, Func<T, TResult>>> cases = new List<Tuple<Func<T, bool>, Func<T, TResult>>>(); private Func<T, TResult> elseFunc; internal PatternMatch(T value) { this.value = value; } /// <summary> /// Specifies a condition to match against, and the value to return /// </summary> /// <param name="matchCondition">The condition to match with.</param> /// <param name="matchResult">The result to return if the condition matches.</param> /// <returns>The current PatternMatch</returns> public PatternMatch<T, TResult> With(Func<T, bool> matchCondition, Func<T, TResult> matchResult) { if (matchCondition == null) throw new ArgumentNullException("matchCondition", "matchCondition is null."); if (matchResult == null) throw new ArgumentNullException("matchResult", "matchResult is null."); cases.Add(Tuple.Create(matchCondition, matchResult)); return this; } /// <summary> /// Specifies a default result if all conditions fail to match. /// </summary> /// <param name="result">The result.</param> /// <returns>The current PatternMatch</returns> public PatternMatch<T, TResult> Else(Func<T, TResult> result) { if (elseFunc != null) throw new InvalidOperationException("Cannot have multiple else cases"); elseFunc = result; return this; } /// <summary> /// Evaluates this pattern match, returning the result of the condition that matches. /// </summary> /// <returns>the match resulg</returns> /// <remarks>If this does not contain a match, this method /// throws an IncompleteMatchException</remarks> public TResult EndMatch() { if (elseFunc != null) cases.Add( Tuple.Create<Func<T, bool>, Func<T, TResult>>(x => true, elseFunc)); foreach (var item in cases) if (item.First(value)) return item.Second(value); throw new IncompleteMatchException("Incomplete pattern match"); }}
由代码可知,Elevate的形成的Function Call Chain中的Context的切换完全是通过返回不同类型的class实现的~在一次一用的环境里,没有问题,但是如果在需要持久化的Case里就会有一些问题~
由于一次一用的实现和持久化的实现的代码几乎没有任何区别~但是Context的切换则会有非常大的不同,如何能够在重用实现的情况下还产生不同的Context(VS的IntelliSence会检测到这种区别,同时在没有强制类型转换的情况下,用户无法调用本不应该调用的Method,例如对一个持久化版的PatternMatch去调用EndMatch()方法,相应的,用户应该调用Apply方法)。
最后我想到了可以利用Interface去标识上下文,而利用同一个class去实现所有的interface,这样,context在切换,但是数据没有复制,调用永远都是一个class。
然而这个时候有存在问题了,由于需要实现ExtensionMethod,为了尽可能的复用代码,我比如让一些方法共集中到一个Interface里,但是同一个方法的调用,返回的Context Interface在一次一用和持久化的场景里,又应该有所不同,于是问题就彻底复杂话了~于是Generic的终极使用方法出现了————Generic的迭代。
public interface IWithContext<T, TResult, TSelf> where TSelf : IWithContext<T, TResult, TSelf>{ TSelf With(Func<T, bool> condition, Func<T, TResult> result); }
注意 TSelf 的限定~华丽丽的限定!还有With方法的返回类型!哈哈~
这里是完整的代码:
好复杂的继承关系!
public class PatternMatch<T, TResult> : IInstantMatchContext<T, TResult>, IWithContext<T, TResult, IInstantMatchContext<T, TResult>>, IElseContext<T, TResult, IEndMatchContext<TResult>>, IEndMatchContext<TResult>, ICompileMatchContext<T, TResult>, IWithContext<T, TResult, ICompileMatchContext<T, TResult>>, IElseContext<T, TResult, IAppliableCompilableContext<T, TResult>>, IAppliableCompilableContext<T, TResult>, ICompilableContext<T, TResult>, IApplyContext<T, TResult>{ #region Constructors internal PatternMatch() { HasValue = false; Cases = new List<MatchCase>(); ElseCase = null; } internal PatternMatch(T input) : this() { this.Input = input; HasValue = true; } #endregion #region Instant Mode Status Fields private T Input; private bool HasValue; #endregion #region Common Runtime Status Fields protected List<MatchCase> Cases; protected Func<T, TResult> ElseCase; #endregion #region Functional Methods protected PatternMatch<T, TResult> With(Func<T, bool> condition, Func<T, TResult> result) { if (condition == null) throw new ArgumentNullException("condition", "condition is null."); if (result == null) throw new ArgumentNullException("result", "result is null."); Cases.Add(new MatchCase(condition, result)); return this; } protected PatternMatch<T, TResult> Else(Func<T, TResult> result) { if (result == null) throw new ArgumentNullException("result", "result is null."); ElseCase = result; return this; } protected TResult EndMatch() { if (!HasValue) throw new InvalidOperationException(); return Apply(Input); } protected IApplyContext<T, TResult> Compile() { return this; } protected TResult Apply(T input) { foreach (var foCase in Cases) { if (foCase.Condition(input)) return foCase.Result(input); } if (ElseCase != null) return ElseCase(input); else throw new IncompletePatternMatchException(); } #endregion #region Nested Entity protected struct MatchCase { public MatchCase(Func<T, bool> condition, Func<T, TResult> result) { this.Condition = condition; this.Result = result; } public Func<T, bool> Condition; public Func<T, TResult> Result; } #endregion #region Contextual Methods #region Instant Mode #region IWithContext<T,TResult,IInstantMatchContext<T,TResult>> Members IInstantMatchContext<T, TResult> IWithContext<T, TResult, IInstantMatchContext<T, TResult>>.With(Func<T, bool> condition, Func<T, TResult> result) { return this.With(condition, result); } #endregion #region IElseContext<T,TResult,IEndMatchContext<TResult>> Members IEndMatchContext<TResult> IElseContext<T, TResult, IEndMatchContext<TResult>>.Else(Func<T, TResult> result) { return this.Else(result); } #endregion #region IEndMatchContext<TResult> Members TResult IEndMatchContext<TResult>.EndMatch() { return this.EndMatch(); } #endregion #endregion #region Compile Mode #region IWithContext<T,TResult,ICompileMatchContext<T,TResult>> Members ICompileMatchContext<T, TResult> IWithContext<T, TResult, ICompileMatchContext<T, TResult>>.With(Func<T, bool> condition, Func<T, TResult> result) { return this.With(condition, result); } #endregion #region IElseContext<T,TResult,IAppliableCompilableContext<T,TResult>> Members IAppliableCompilableContext<T, TResult> IElseContext<T, TResult, IAppliableCompilableContext<T, TResult>>.Else(Func<T, TResult> result) { return this.Else(result); } #endregion #region ICompilableContext<T,TResult> Members IApplyContext<T, TResult> ICompilableContext<T, TResult>.Compile() { return this.Compile(); } #endregion #region IApplyContext<T,TResult> Members TResult IApplyContext<T, TResult>.Apply(T input) { return this.Apply(input); } #endregion #endregion #endregion}
public class InitialWithContext<T> : IWithContext<T>
{ private T input; internal InitialWithContext(T input) { this.input = input; } #region IWithContext<T> Members public IInstantMatchContext<T, TResult> With<TResult>(Func<T, bool> condition, Func<T, TResult> result) { return (new PatternMatch<T, TResult>(input) as IInstantMatchContext<T, TResult>).With(condition, result); } #endregion}
public static class PatternMatch
{ public static <span
Posted via email from 米良的草窝
没有评论:
发表评论