局部变量和表达式树

我正在学习C#中的表达式树。

我现在被困了一段时间:

string filterString = "ruby"; Expression<Func> expression = x => x == filterString; 

如何通过代码构造此表达式? 没有示例如何捕获局部变量。 这个很简单:

 Expression<Func> expression = x => x == "ruby"; 

这将是:

 ParameterExpression stringParam = Expression.Parameter(typeof(string), "x"); Expression constant = Expression.Constant("ruby"); BinaryExpression equals = Expression.Equal(stringParam, constant); Expression<Func> lambda1 = Expression.Lambda<Func>( equals, new ParameterExpression[] { stringParam }); 

调试器为(x => x == filterString)打印以下内容:

{x =>(x == value(Predicate.Program + c__DisplayClass3).filterString)}

感谢您对此主题有所了解。

捕获局部变量实际上是通过将局部变量“提升”到编译器生成的类的实例变量中来执行的。 C#编译器在适当的时间创建额外类的新实例,并将对局部变量的任何访问权限更改为相关实例中实例变量的访问权限。

因此表达式树需要是实例中的字段访问 – 实例本身是通过ConstantExpression提供的。

处理如何创建表达式树的最简单方法通常是在lambda表达式中创建类似的东西,然后查看Reflector中生成的代码,将优化级别调低,以便Reflector不会将其转换回lambda表达式。

此代码将表达式包装在闭包块中,该闭包块将局部变量视为常量。

  string filterString = "ruby"; var filterStringParam = Expression.Parameter(typeof(string), "filterString"); var stringParam = Expression.Parameter(typeof(string), "x"); var block = Expression.Block( // Add a local variable. new[] { filterStringParam }, // Assign a constant to the local variable: filterStringParam = filterString Expression.Assign(filterStringParam, Expression.Constant(filterString, typeof(string))), // Compare the parameter to the local variable Expression.Equal(stringParam, filterStringParam)); var x = Expression.Lambda>(block, stringParam).Compile(); 

一个老问题,但我在试图为Linq-to-entities(L2E)做类似的构建表达式时遇到它在这种情况下你不能使用Expression.Block因为它无法解析为SQL。

以下是Jon的回答后的一个明确的例子,它可以与L2E一起使用。 创建一个帮助器类来包含filter的值:

 class ExpressionScopedVariables { public String Value; } 

因此构建树:

 var scope = new ExpressionScopedVariables { Value = filterString}; var filterStringExp = Expression.Constant(scope); var getVariable = typeof(ExpressionScopedVariables).GetMember("Value")[0]; var access = Expression.MakeMemberAccess(filterStringExp, getVariable); 

然后使用成员访问表达式替换原始代码中的常量:

 BinaryExpression equals = Expression.Equal(stringParam, access); Expression> lambda1 = Expression.Lambda>( equals, new ParameterExpression[] { stringParam });