我是c#中表达式API的新手。假设我有两个双参数x和y的表达式。我想将这个表达式包装成另一个表达式,我可以传递一个double数组而不是两个参数。我尝试了以下(我在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) ....
显然,我无法将值绑定到参数,但我不知道如何解决这个问题。任何提示/建议将不胜感激!感谢
编辑:基本上我想要实现的是包装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(双x,双y)说我得到一个输入表达式(让我们称之为Foo_Expression),我知道它有两个参数。我想将此表达式包装在另一个表达式中(相当于上面的WrappedFoo),以便新表达式采用单个双数组参数,然后分配这些参数以调用Foo_Expression。我不确定我是否可以用表达式来做到这一点。
答案 0 :(得分:1)
就像普通的C#代码一样,必须声明Expression
中的变量。在overload of Block
that you're using中,第一个参数列出了声明的变量。因此,您声明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)
答案 1 :(得分:0)
参数x
和y
在您之前的lambda中编译,并且不会传递到第二个Expression.Lambda
调用,但它似乎并不像您想要的那样,因为那时你有一个带有method(double[], double, double)
方法签名的表达式。您可能希望将其他x2
和y2
定义为本地人,然后您可以将其作为参数传递给第一个编译的lambda。
如果您澄清我可能会进一步提供帮助,那么我并非100%清楚您要完成的任务。有时,它帮助我编写出我希望它们如何表现的函数,然后将它们转换为表达式。