作为funcletizer的一部分,我希望用它们的计算常量替换不包含参数的c#表达式:
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)
我知道我可以通过将表达式包装在lambda表达式中并调用它来计算表达式:
object result = Expression.Lambda(ex2).Compile().DynamicInvoke();
// result == -100
当表达式包含未绑定的参数时,如上面的ex1
,这当然会失败,抛出InvalidOperationException,因为我没有提供任何参数。
如何检查表达式是否包含此类参数?
我目前的解决方案涉及try {} catch(InvoalidOperationException),但这似乎是一种非常不优雅且容易出错的方式:
// 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;
}
}
答案 0 :(得分:5)
当然,大多数事情都是可能的。这里有两个独立的事情:
代码:
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;
}
}
}
答案 1 :(得分:1)
在这种情况下,您可以将Expression
转换为LambdaExpression
,看看它是否有参数。
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;
}