局部变量和表达式树

yon*_*bat 25 c# linq lambda

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

我现在被困了一段时间了:

string filterString = "ruby";
Expression<Func<string, bool>> expression = x => x == filterString;
Run Code Online (Sandbox Code Playgroud)

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

Expression<Func<string, bool>> expression = x => x == "ruby";
Run Code Online (Sandbox Code Playgroud)

这将是:

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 });
Run Code Online (Sandbox Code Playgroud)

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

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

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

Jon*_*eet 32

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

因此,表达式树然后需要在实例中进行字段访问 - 实例本身通过a提供ConstantExpression.

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

  • 像`var hoistedConstant = Expression.Property(Expression.Constant(new {Value = filterString}),"Value");`应该这样做 (3认同)
  • 谢谢.查看生成的MSIL代码的提示非常有用. (2认同)

Nes*_*cio 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();
Run Code Online (Sandbox Code Playgroud)


Jam*_*ose 5

这是一个老问题,但是当我尝试为Linq-to-entities(L2E)做类似的构建表达式时,我遇到了这个问题。在这种情况下,您将无法使用Expression.Block,因为无法将其解析为SQL。

在Jon的回答之后,这是一个明确的示例,该示例适用于L2E。创建一个帮助器类以包含过滤器的值:

class ExpressionScopedVariables
{
    public String Value;
}
Run Code Online (Sandbox Code Playgroud)

这样构建树:

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

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

BinaryExpression equals = Expression.Equal(stringParam, access);
Expression<Func<string, bool>> lambda1 =
    Expression.Lambda<Func<string, bool>>(
        equals,
        new ParameterExpression[] { stringParam });
Run Code Online (Sandbox Code Playgroud)