如何评估ExpressionVisitor中的表达式?

在执行Expression之前,我需要使用ExpressionVisitor来分析它。 根据我的需要,我需要评估Divide表达式的正确部分,但我不知道该怎么做。 这是我的示例代码:

internal class RulesChecker : ExpressionVisitor { private readonly object data; public RulesChecker(object data) { this.data = data; } protected override Expression VisitBinary(BinaryExpression node) { if (node.NodeType == ExpressionType.Divide) { var rightExpression = node.Right; // compile the right expression and get his value } return base.VisitBinary(node); } } 

假设我有这个代码来评估:

 Expression<Func> expression = x => xA / (xB + xC); var rulesChecker = new RulesChecker(data); rulesChecker.Visit(expression); 

在VisitBinary函数中,我将收到一个节点,该节点将包含除法运算的左右部分。 我的问题是,我如何评估我将在操作的正确部分获得的价值?

我认为这个问题最困难的部分是处理变量。 所以我首先要替换常量变量。 之后,您只需执行并更新Expression。

 using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace WindowsFormsApplication1 { static class Program { [STAThread] static void Main() { var value1 = 1; var value2 = 2; var value3 = new { MyValue = 3 }; var data = new DataInfo { A = 10, B = 1, C = -1 }; Expression> expression = x => xA / (xB + xC) + (value1 + value2) + value3.MyValue; // create a list of variables that will be used when evaluating the expression var variables = new Dictionary(); // add the root object variables.Add(data.GetType(), data); // find variables that are referenced in the expression var finder = new VariablesFinder(variables); finder.Visit(expression); // replace variables with ConstantExpressions var visitor = new VariableReplacer(variables); var newExpression = visitor.Visit(expression); var rulesChecker = new RulesChecker(); var checkedExpression = rulesChecker.Visit(newExpression); } } internal class RulesChecker : ExpressionVisitor { protected override Expression VisitBinary(BinaryExpression node) { if (node.NodeType == ExpressionType.Divide) { var rightBinaryExpression = node.Right as BinaryExpression; if (rightBinaryExpression != null) { node = node.Update(node.Left, node.Conversion, this.Execute(rightBinaryExpression)); } } return base.VisitBinary(node); } private Expression Execute(BinaryExpression node) { var lambda = Expression.Lambda(node); dynamic func = lambda.Compile(); var result = func(); return Expression.Constant(result, result.GetType()); } } internal class VariableReplacer : ExpressionVisitor { private readonly Dictionary _variables; public VariableReplacer(Dictionary variables) { this._variables = variables; } protected override Expression VisitMember(MemberExpression node) { return this.HandleProperty(node) ?? this.HandleField(node) ?? node; } private Expression HandleField(MemberExpression memberExpression) { var fieldInfo = memberExpression.Member as FieldInfo; if (fieldInfo != null) { var value = fieldInfo.GetValue(this.GetVarialbe(fieldInfo)); return Expression.Constant(value, fieldInfo.FieldType); } return null; } private Expression HandleProperty(MemberExpression memberExpression) { var propertyInfo = memberExpression.Member as PropertyInfo; if (propertyInfo != null) { var value = propertyInfo.GetValue(this.GetVarialbe(propertyInfo), null); return Expression.Constant(value, propertyInfo.PropertyType); } return null; } private object GetVarialbe(MemberInfo memberInfo) { return this._variables[memberInfo.DeclaringType]; } } internal class VariablesFinder : ExpressionVisitor { private readonly Dictionary _variables; public VariablesFinder(Dictionary variables) { this._variables = variables; } protected override Expression VisitConstant(ConstantExpression node) { this.AddVariable(node.Type, node.Value); return base.VisitConstant(node); } private void AddVariable(Type type, object value) { if (type.IsPrimitive) { return; } if (this._variables.Keys.Contains(type)) { return; } this._variables.Add(type, value); var fields = type.GetFields().Where(x => !x.FieldType.IsPrimitive).ToList(); foreach (var field in fields) { this.AddVariable(field.FieldType, field.GetValue(value)); } } } class DataInfo { public int A { get; set; } public int B { get; set; } public int C { get; set; } public int D; } } 

通常,您可以使用此方法来计算lambda表达式(并传递):

 protected object EvaluateExpression(Expression expression) { var lambda = Expression.Lambda(expression); var compiled = lambda.Compile(); var value = compiled.DynamicInvoke(null); return value; } 

但是,在您的情况下,这将无效,因为您尝试评估的表达式取决于x ,无法对其进行评估,除非您为其指定具体值,如Wiktor建议的那样。

要为参数指定值,您需要修改方法:

 protected static object EvaluateExpression(Expression expression, ParameterExpression parameterX) { var lambda = Expression.Lambda(expression, parameterX); var compiled = lambda.Compile(); return compiled.DynamicInvoke(5); // 5 here is the actual parameter value. change it to whatever you wish } 

但是,此版本的方法必须将ExpressionParameter对象作为参数,该对象表示表达式中的x ,以便它知道如何处理传递给DynamicInvoke()的值。

为了获得适当的ExpressionParameter对象,您需要访问根表达式,而不是访问其中一个节点,因此我想在访问者中执行它会很尴尬。

如果我理解正确,您希望将访问表达式的结果返回到已修改的表达式树,该表达式树具有以某种方式评估的分区的右侧。 您将使用BinaryExpressionUpdate方法将右侧节点替换为您的值:

 protected override Expression VisitBinary(BinaryExpression node) { if (node.NodeType == ExpressionType.Divide) { var rightExpression = node.Right; // compile the right expression and get his value var newRightExpression = Evaluate(rightExpression); return node.Update(node.Left, node.Conversion, newRightExpression); } return base.VisitBinary(node); } 

在此代码中, newRightExpression需要是inheritance自Expression的类型。 如果正确的节点评估为double值,那么您需要将它包装在ConstantExpression

 double rightValue = EvaluateToDouble(rightExpression); var newRightExpression = Expression.Constant(rightValue, typeof(double)); 

我认为@ w0lf是正确的道路。

要从访问者中获取参数,您需要覆盖VisitLambda。 最好的方法是覆盖访问者的每个可用方法,并将参数传递给您的所有方法。

另一种方法是保存最新的参数。 实际上,参数数组在整个lambda表达式中都是相同的。

这是一段代码,将除法运算的右侧乘以2并将其替换为原始表达式,假设右侧和左侧是double类型。

 class Program { static void Main(string[] args) { Expression> abc = v => 1.0d * v.Ticks / (v.Month + v.Minute); MyExpressionVisitor mev = new MyExpressionVisitor(DateTime.Now); var ret = mev.Visit(abc); } } internal class MyExpressionVisitor : ExpressionVisitor { IEnumerable _parameters = null; object _parameterValue = null; public MyExpressionVisitor(object valueOfParameter) { _parameterValue = valueOfParameter; } protected override Expression VisitLambda(Expression node) { _parameters = node.Parameters; return base.VisitLambda(node); } protected override Expression VisitBinary(BinaryExpression node) { if (_parameters != null) { // Evaluate right node. var value = EvaluateExpression(node.Right, _parameters.ToArray(), _parameterValue); // substitute value with 2 * value. var newRight = Expression.Constant(value * 2); var ret = node.Update(node.Left, node.Conversion, newRight); return ret; } return base.VisitBinary(node); } protected double EvaluateExpression(Expression expression, ParameterExpression[] parameters, object parameterValue) { var lambda = Expression.Lambda(expression, parameters); var compiled = lambda.Compile(); var value = compiled.DynamicInvoke(parameterValue); return Convert.ToDouble(value); } }