Check if expression has free parameters

As part of the funcletizer, I want to replace C # expressions that do not contain parameters with their evaluated constants:

double d = 100.0; Expression<Func<double, double>> ex1 = x => -x; Expression<Func<double>> ex2 = () => -d; Expression result; result = Funcletize(ex1); // should return ex1 unmodified result = Funcletize(ex2); // should return Expression.Constant(-100.0) 

I know that I can evaluate an expression by wrapping it in a lambda expression and causing the following:

 object result = Expression.Lambda(ex2).Compile().DynamicInvoke(); // result == -100 

When an expression contains unrelated parameters, like ex1 above, this of course crashes throwing an InvalidOperationException, as I did not give any parameters. How to check if an expression contains such parameters?

My current solution includes try {} catch (InvoalidOperationException), but this seems a very inelegant and error-prone way:

 // this works; by catching InvalidOperationException public static Expression Funcletize(Expression ex) { try { // Compile() will throw InvalidOperationException, // if the expression contains unbound parameters var lambda = Expression.Lambda(ex).Compile(); Object value = lambda.DynamicInvoke(); return Expression.Constant(value, ex.Type); } catch (InvalidOperationException) { return ex; } } 
+4
source share
2 answers

Of course, most things are possible. There are two separate things here:

  • removal of unused parameters by tracking, which are visible in the expression
  • evaluation and nesting of captured variables (emphasis: this is a semantic change) - what we are doing is trying to recognize the field โ†’ [field โ†’] ... field-> template (although the code shown may actually perform false positives here in some cases)

the code:

 using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; static class Program { static void Main() { double d = 100; Expression<Func<double, double>> ex1 = x => -x; Expression<Func<double>> ex2 = () => -d; var result1 = Demungify(ex1); // (x) => -x var result2 = Demungify(ex2); // () => -100 } public static LambdaExpression Demungify(LambdaExpression ex) { var visitor = new Demungifier(); var newBody = visitor.Visit(ex.Body); var args = ex.Parameters.Where(visitor.WasSeen).ToArray(); var lambda = Expression.Lambda(newBody, args); if (!args.Any() && !(lambda.Body is ConstantExpression)) { // evaluate that! object result = lambda.Compile().DynamicInvoke(); lambda = Expression.Lambda(Expression.Constant(result, newBody.Type)); } return lambda; } class Demungifier : ExpressionVisitor { readonly HashSet<ParameterExpression> parameters = new HashSet<ParameterExpression>(); public bool WasSeen(ParameterExpression param) { return parameters.Contains(param); } protected override Expression VisitParameter(ParameterExpression node) { parameters.Add(node); return base.VisitParameter(node); } protected override Expression VisitMember(MemberExpression node) { object value; if(TryEvaluate(node, out value)) { return Expression.Constant(value, ((FieldInfo)node.Member).FieldType); } return base.VisitMember(node); } bool TryEvaluate(Expression expression, out object value) { if(expression == null) { value = null; return true; } if(expression.NodeType == ExpressionType.Constant) { value = ((ConstantExpression)expression).Value; return true; } // captured variables are always fields, potentially of fields of fields // eventually terminating in a ConstantExpression that is the capture-context MemberExpression member; if(expression.NodeType == ExpressionType.MemberAccess && (member= (MemberExpression)expression).Member.MemberType == System.Reflection.MemberTypes.Field) { object target; if(TryEvaluate(member.Expression, out target)) { value = ((FieldInfo)member.Member).GetValue(target); return true; } } value = null; return false; } } } 
+5
source

In this case, you can drop Expression to LambdaExpression and see if it has options.

 public static Expression Funcletize(Expression ex) { var l = ex as LambdaExpression; if (l != null && l.Parameters.Count == 0) { var lambda = Expression.Lambda(ex).Compile(); Object value = lambda.DynamicInvoke(); return Expression.Constant(value, ex.Type); } return ex; } 
+1
source

Source: https://habr.com/ru/post/1501442/


All Articles