PartialEvaluator.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq.Expressions;
  4. namespace Common.LambdaToSQL
  5. {
  6. /// <summary>
  7. ///重写一个表达示树,并将其中引用变量转换成常量
  8. ///去除所附加的类信息
  9. /// </summary>
  10. internal static class PartialEvaluator
  11. {
  12. /// <summary>
  13. /// Performs evaluation & replacement of independent sub-trees
  14. /// </summary>
  15. /// <param name="expression">The root of the expression tree.</param>
  16. /// <returns>A new tree with sub-trees evaluated and replaced.</returns>
  17. public static Expression Eval(Expression expression)
  18. {
  19. return Eval(expression, null);
  20. }
  21. /// <summary>
  22. /// Performs evaluation & replacement of independent sub-trees
  23. /// </summary>
  24. /// <param name="expression">The root of the expression tree.</param>
  25. /// <param name="fnCanBeEvaluated">A function that decides whether a given expression node can be part of the local function.</param>
  26. /// <returns>A new tree with sub-trees evaluated and replaced.</returns>
  27. private static Expression Eval(Expression expression, Func<Expression, bool> fnCanBeEvaluated)
  28. {
  29. if (fnCanBeEvaluated == null)
  30. fnCanBeEvaluated = PartialEvaluator.CanBeEvaluatedLocally;
  31. return SubtreeEvaluator.Eval(Nominator.Nominate(fnCanBeEvaluated, expression), expression);
  32. }
  33. private static bool CanBeEvaluatedLocally(Expression expression)
  34. {
  35. return expression.NodeType != ExpressionType.Parameter;
  36. }
  37. /// <summary>
  38. /// Evaluates & replaces sub-trees when first candidate is reached (top-down)
  39. /// </summary>
  40. private class SubtreeEvaluator : ExpressionVisitor
  41. {
  42. private readonly HashSet<Expression> _candidates;
  43. private SubtreeEvaluator(HashSet<Expression> candidates)
  44. {
  45. this._candidates = candidates;
  46. }
  47. internal static Expression Eval(HashSet<Expression> candidates, Expression exp)
  48. {
  49. return new SubtreeEvaluator(candidates).Visit(exp);
  50. }
  51. protected override Expression Visit(Expression exp)
  52. {
  53. if (exp == null)
  54. {
  55. return null;
  56. }
  57. if (this._candidates.Contains(exp))
  58. {
  59. return this.Evaluate(exp);
  60. }
  61. return base.Visit(exp);
  62. }
  63. private Expression Evaluate(Expression e)
  64. {
  65. Type type = e.Type;
  66. // check for nullable converts & strip them
  67. if (e.NodeType == ExpressionType.Convert)
  68. {
  69. var u = (UnaryExpression)e;
  70. if (TypeHelper.GetNonNullableType(u.Operand.Type) == TypeHelper.GetNonNullableType(type))
  71. {
  72. e = ((UnaryExpression)e).Operand;
  73. }
  74. }
  75. // if we now just have a constant, return it
  76. if (e.NodeType == ExpressionType.Constant)
  77. {
  78. var ce = (ConstantExpression)e;
  79. // if we've lost our nullable typeness add it back
  80. if (e.Type != type && TypeHelper.GetNonNullableType(e.Type) == TypeHelper.GetNonNullableType(type))
  81. {
  82. e = ce = Expression.Constant(ce.Value, type);
  83. }
  84. return e;
  85. }
  86. var me = e as MemberExpression;
  87. if (me != null)
  88. {
  89. // member accesses off of constant's are common, and yet since these partial evals
  90. // are never re-used, using reflection to access the member is faster than compiling
  91. // and invoking a lambda
  92. var ce = me.Expression as ConstantExpression;
  93. if (ce != null)
  94. {
  95. return Expression.Constant(me.Member.GetValue(ce.Value), type);
  96. }
  97. }
  98. if (type.IsValueType)
  99. {
  100. e = Expression.Convert(e, typeof(object));
  101. }
  102. Expression<Func<object>> lambda = Expression.Lambda<Func<object>>(e);
  103. #if NOREFEMIT
  104. Func<object> fn = ExpressionEvaluator.CreateDelegate(lambda);
  105. #else
  106. Func<object> fn = lambda.Compile();
  107. #endif
  108. return Expression.Constant(fn(), type);
  109. }
  110. }
  111. /// <summary>
  112. /// Performs bottom-up analysis to determine which nodes can possibly
  113. /// be part of an evaluated sub-tree.
  114. /// </summary>
  115. private class Nominator : ExpressionVisitor
  116. {
  117. private readonly Func<Expression, bool> _fnCanBeEvaluated;
  118. private readonly HashSet<Expression> _candidates;
  119. private bool _cannotBeEvaluated;
  120. private Nominator(Func<Expression, bool> fnCanBeEvaluated)
  121. {
  122. this._candidates = new HashSet<Expression>();
  123. this._fnCanBeEvaluated = fnCanBeEvaluated;
  124. }
  125. internal static HashSet<Expression> Nominate(Func<Expression, bool> fnCanBeEvaluated, Expression expression)
  126. {
  127. Nominator nominator = new Nominator(fnCanBeEvaluated);
  128. nominator.Visit(expression);
  129. return nominator._candidates;
  130. }
  131. protected override Expression VisitConstant(ConstantExpression c)
  132. {
  133. return base.VisitConstant(c);
  134. }
  135. protected override Expression Visit(Expression expression)
  136. {
  137. if (expression != null)
  138. {
  139. bool saveCannotBeEvaluated = this._cannotBeEvaluated;
  140. this._cannotBeEvaluated = false;
  141. base.Visit(expression);
  142. if (!this._cannotBeEvaluated)
  143. {
  144. if (this._fnCanBeEvaluated(expression))
  145. {
  146. this._candidates.Add(expression);
  147. }
  148. else
  149. {
  150. this._cannotBeEvaluated = true;
  151. }
  152. }
  153. this._cannotBeEvaluated |= saveCannotBeEvaluated;
  154. }
  155. return expression;
  156. }
  157. }
  158. }
  159. }