如何在c#表达式中设置参数

本文关键字:设置 参数 表达式 | 更新日期: 2023-09-27 18:08:04

我是c#表达式API的新手。假设我有一个表达式,有两个双精度形参x和y。我想把这个表达式包装成另一个表达式,在这个表达式中我可以传递一个双精度数组,而不是两个参数。我已经尝试了以下(我正在f#中测试它,因此语法)

let x = Expression.Parameter(typeof<double>,"x")
let y = Expression.Parameter(typeof<double>,"y")
let givenExpr = Expression.Add(x,y)
// I know that I can evaluate givenExpr as follows
let myLambda1 = Expression.Lambda<Func<double,double,double>>(givenExpr, [| x; y |]).Compile()
// and this works
myLambda1.Invoke(1.0,1.0)

// But if I instead want to pass x and y in an array and then "assign" them, this doesn't work
let inputArray = Expression.Parameter(typeof<double[]>,"inputs")
let result = Expression.Parameter(typeof<double>,"result")
let blockStatements = new List<Expression>()
blockStatements.Add( Expression.Assign(x,Expression.ArrayAccess(inputArray,Expression.Constant(0))) );
blockStatements.Add( Expression.Assign(y,Expression.ArrayAccess(inputArray,Expression.Constant(1))) );
blockStatements.Add( Expression.Assign(result, givenExpr) );
let block = Expression.Block( [| result |], blockStatements )
let arrayLambda = Expression.Lambda<Func<double[],double>>(block, [|inputArray|]).Compile()
// This blows up 
arrayLambda.Invoke( [|1.0; 1.0|]) 
// with 
// System.InvalidOperationException: variable 'x' of type 'System.Double' referenced from scope '', but it is not defined
//   at System.Linq.Expressions.Compiler.VariableBinder.Reference(ParameterExpression node, VariableStorageKind storage)
//   at System.Linq.Expressions.Compiler.VariableBinder.VisitParameter(ParameterExpression node) ....

显然,我不能将值绑定到参数,但我不知道如何解决这个问题。任何提示/建议将不胜感激!由于

EDIT:基本上我想要实现的是包装n个变量的给定函数(例如下面的Foo),以便包装的函数代替使用double[]类型的单个参数。对于普通方法,我可以这样做(例如2个参数)

double Foo( double x, double y);
double WrappedFoo( double [] args ){
    double x = args[0];
    double y = args[1];
    return Foo( x, y );
}

我认为表达式基本上是一个函数体(这可能是不正确的!)所以不是Foo(double x, double y),假设我得到一个输入表达式(我们叫它Foo_Expression),我知道它有两个参数。我想把这个表达式包装在另一个表达式中(相当于上面的WrappedFoo),以便新的表达式接受单个双数组参数,然后分发这些参数以调用Foo_Expression。

如何在c#表达式中设置参数

就像在普通的c#代码中一样,必须声明Expression中的变量。在您正在使用的Block重载中,第一个参数列出了声明的变量。因此,您声明的是result,而不是xy

它在你的第一个版本中工作,因为xy是参数,而不是局部变量。

所以,修复是:

let block = Expression.Block( [| x; y; result |], blockStatements )

这是有效的,但大多是偶然的。你没有说result是应该返回的值,实际返回的值是最后一个表达式的值(在这方面,Expression更像f#而不是c#)。

因此,修复后的代码相当于:
double WrappedFoo(double[] inputs)
{
    double x;
    double y;
    double result;
    x = inputs[0];
    y = inputs[1];
    return (result = Foo(x, y));
}

要获得更像您所描述的代码,请删除result变量并将最后一条语句更改为您想要返回的表达式:

blockStatements.Add(givenExpr)

参数xy在之前的lambda中编译,并且没有传递到第二个Expression.Lambda调用中,但无论如何,它似乎不像您想要的那样,因为那样您就会有一个具有method(double[], double, double)方法签名的表达式。您可能想要定义额外的x2y2作为局部变量,然后将它们作为参数传递给第一个编译的lambda。

我不是百分之百清楚你想要完成什么,如果你能说清楚,我可能能进一步帮助你。有时,它可以帮助我写出我想要它们的行为,然后将它们转换为表达式。