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

时间:2014-06-10 18:07:05

标签: c# .net linq-expressions

我是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。我不确定我是否可以用表达式来做到这一点。

2 个答案:

答案 0 :(得分:1)

就像普通的C#代码一样,必须声明Expression中的变量。在overload of Block that you're using中,第一个参数列出了声明的变量。因此,您声明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)

答案 1 :(得分:0)

参数xy在您之前的lambda中编译,并且不会传递到第二个Expression.Lambda调用,但它似乎并不像您想要的那样,因为那时你有一个带有method(double[], double, double)方法签名的表达式。您可能希望将其他x2y2定义为本地人,然后您可以将其作为参数传递给第一个编译的lambda。

如果您澄清我可能会进一步提供帮助,那么我并非100%清楚您要完成的任务。有时,它帮助我编写出我希望它们如何表现的函数,然后将它们转换为表达式。