【问题标题】:Local variable and expression trees局部变量和表达式树
【发布时间】:2011-08-28 11:23:28
【问题描述】:

我正在学习 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 => (x == value(Predicate.Program+c__DisplayClass3).filterString)}

感谢您对这个话题有所了解。

【问题讨论】:

    标签: c# linq lambda


    【解决方案1】:

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

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

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

    【讨论】:

    • 谢谢。查看生成的 MSIL 代码的提示非常有用。
    • var hoistedConstant = Expression.Property(Expression.Constant(new {Value = filterString}), "Value"); 之类的东西应该这样做
    • @Appetere Expression.Constant(filterString) 怎么样?诚然,它不会反映对变量的更改,但您的建议也不会。
    • 投了赞成票,但 Jame 的回答对我来说更中肯。
    • @Appetere 这是这里最好的答案,非常感谢
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 这对我帮助很大,谢谢!我的要求不同,但创建“范围”类并从中创建表达式的概念朝着正确的方向发展。
      • 非常感谢!这真的很有帮助
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多