参数上下文不变的复杂表达式树

Complex Expression Tree with no changing context of param

我需要像这样动态生成表达式:

Expression<Func<MyClass, bool>> expr = x => (x.SomeField.CompareTo(someValue) <= 0);

尝试这样做:

var paramExpr = Expression.Parameter(typeof(MyClass), "x");
Expression<Func<MyClass, FieldType>> pathToField = x => x.SomeField;
Expression path = pathToField;
if (!(path is LambdaExpression lambdaMember))
    throw ...;
Expression valueExpr = Expression.Constant(someValue);
var bodyExpr = Expression.LessThanOrEqual(Expression.Call(lambdaMember.Body, "CompareTo", null, valueExpr ), Expression.Constant(0));
return Expression.Lambda<Func<MyClass, FieldType>>(bodyExpr, paramExpr);

但尝试编译时总是出错:

variable 'x' of type 'MyClass' referenced from scope '', but it is not defined

我怎样才能正确地做到这一点?

这里的问题是您使用的是 lambdaMember.Body,它引用了 x => x.SomeField 中的 x - 但是因为您 只使用了 .Body,未定义 - 与 Expression.Parameter(typeof(MyClass), "x");

中的 x 无关

一般情况下,这里有2个选项:

  • 调用整个 lambda(即lambdaMember,而不是lambdaMember.Body)——将参数传递给用于参数
  • 在运行时使用 ExpressionVisitor 重写内部 lambda - 用任何你想用作参数 - 大概是 paramExpr

第一个选项更简单,就是 Expression.Invoke:

var bodyExpr = Expression.LessThanOrEqual(
    Expression.Call(Expression.Invoke(lambdaMember, paramExpr),
    "CompareTo", null, valueExpr), Expression.Constant(0));

注意:在这种情况下 第三个选项,因为它是一个相对简单的示例 - 您可以从 inner 表达式并使用它 而不是 声明 paramExpr 作为新参数表达式:

var paramExpr = lambdaMember.Parameters.Single();
Expression valueExpr = Expression.Constant(someValue);
var bodyExpr = Expression.LessThanOrEqual(
    Expression.Call(lambdaMember.Body,
    "CompareTo", null, valueExpr), Expression.Constant(0));
return Expression.Lambda<Func<MyClass, FieldType>>(bodyExpr, lambdaMember.Parameters);