using System; using System.Collections.Generic; using System.Linq.Expressions; namespace Hncore.Infrastructure.EntitiesExtension { public class PartialEvaluator : ExpressionVisitor { private Func m_fnCanBeEvaluated; private HashSet m_candidates; public PartialEvaluator() : this(CanBeEvaluatedLocally) { } public PartialEvaluator(Func fnCanBeEvaluated) { this.m_fnCanBeEvaluated = fnCanBeEvaluated; } public Expression Eval(Expression exp) { this.m_candidates = new Nominator(this.m_fnCanBeEvaluated).Nominate(exp); return this.Visit(exp); } protected override Expression Visit(Expression exp) { if (exp == null) { return null; } if (this.m_candidates.Contains(exp)) { return this.Evaluate(exp); } return base.Visit(exp); } private Expression Evaluate(Expression e) { if (e.NodeType == ExpressionType.Constant) { return e; } LambdaExpression lambda = Expression.Lambda(e); Delegate fn = lambda.Compile(); return Expression.Constant(fn.DynamicInvoke(null), e.Type); } private static bool CanBeEvaluatedLocally(Expression exp) { return exp.NodeType != ExpressionType.Parameter; } #region Nominator /// /// Performs bottom-up analysis to determine which nodes can possibly /// be part of an evaluated sub-tree. /// private class Nominator : ExpressionVisitor { private Func m_fnCanBeEvaluated; private HashSet m_candidates; private bool m_cannotBeEvaluated; internal Nominator(Func fnCanBeEvaluated) { this.m_fnCanBeEvaluated = fnCanBeEvaluated; } internal HashSet Nominate(Expression expression) { this.m_candidates = new HashSet(); this.Visit(expression); return this.m_candidates; } protected override Expression Visit(Expression expression) { if (expression != null) { bool saveCannotBeEvaluated = this.m_cannotBeEvaluated; this.m_cannotBeEvaluated = false; base.Visit(expression); if (!this.m_cannotBeEvaluated) { if (this.m_fnCanBeEvaluated(expression)) { this.m_candidates.Add(expression); } else { this.m_cannotBeEvaluated = true; } } this.m_cannotBeEvaluated |= saveCannotBeEvaluated; } return expression; } } #endregion } }