局部变量和表达式树

时间:2011-08-28 11:23:29

标签: c# linq lambda

我正在用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 =&gt; x == filterString)打印以下内容:

  

{x =&gt; (x ==   值(Predicate.Program + LT;&GT; c__DisplayClass3).filterString)}

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

3 个答案:

答案 0 :(得分:30)

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

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

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

答案 1 :(得分:7)

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

 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();

答案 2 :(得分:5)

一个老问题,但我在尝试为Linq-to-entities(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 });