如何在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#代码中一样,必须声明Expression
中的变量。在您正在使用的Block
重载中,第一个参数列出了声明的变量。因此,您声明的是result
,而不是x
或y
。
它在你的第一个版本中工作,因为x
和y
是参数,而不是局部变量。
所以,修复是:
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)
参数x
和y
在之前的lambda中编译,并且没有传递到第二个Expression.Lambda
调用中,但无论如何,它似乎不像您想要的那样,因为那样您就会有一个具有method(double[], double, double)
方法签名的表达式。您可能想要定义额外的x2
和y2
作为局部变量,然后将它们作为参数传递给第一个编译的lambda。