2011-08-28 37 views
14

我正在学习C#中的表达式树。局部变量和表达式树

我现在坚持了一会儿:

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

我如何构建通过代码这体现在哪里?没有示例如何捕获局部变量。这个人是容易的:

Expression<Func<string, bool>> expression = x => x == "ruby"; 

这将是:

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

调试器对打印(X => X == filterString)执行以下操作:

{X =>(值为(Predicate.Program + <> c__DisplayClass3).filterString)}

感谢您对此主题的阐述。

回答

24

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

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

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

+2

谢谢。查看生成的MSIL代码的提示非常有用。 – yonexbat

+3

'var hoistedConstant = Expression.Property(Expression.Constant(new {Value = filterString}),“Value”);'应该这样做 – Appetere

+1

@Appetere Expression.Constant(filterString)''怎么样?诚然,它不会反映变量的变化,但您的建议也不会。 –

5

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

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<Func<string, bool>>(block, stringParam).Compile(); 
0

一个老问题,但试图做的LINQ到实体(L2E)类似的东西建筑表情在这种情况下,你不能使用Expression.Block,因为它不能被解析到SQL当我来到它。

以下是Jon的答案后的一个明确示例,它可以与L2E一起使用。创建一个帮助类来包含所述过滤器的值:

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<Func<string, bool>> lambda1 = 
    Expression.Lambda<Func<string, bool>>(
     equals, 
     new ParameterExpression[] { stringParam });