本地变量和表达式树
本地变量和表达式树
我正在学习C#中的表达式树。
我现在遇到了一些困难:
string filterString = "ruby"; Expression> expression = x => x == filterString;
我该如何通过代码构建这个表达式?没有示例来捕获局部变量。这个很简单:
Expression> expression = x => x == "ruby";
可以这样做:
ParameterExpression stringParam = Expression.Parameter(typeof(string), "x"); Expression constant = Expression.Constant("ruby"); BinaryExpression equals = Expression.Equal(stringParam, constant); Expression> lambda1 = Expression.Lambda >( equals, new ParameterExpression[] { stringParam });
调试器打印出(x => x == filterString)的内容如下:
{x => (x ==
value(Predicate.Program+<>c__DisplayClass3).filterString)}
感谢对这个主题的解释。
这段代码中的问题是将表达式包装在一个闭包块中,将局部变量视为常量。原因是在表达式树中,局部变量被视为常量,而不是作为变量进行处理。这可能导致一些意想不到的结果,特别是在使用局部变量进行比较时。
解决方法是创建一个新的参数来表示局部变量,而不是将其视为常量。这样可以确保在表达式树中正确地处理局部变量。
以下是修复问题的代码:
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 the parameter to the local variable: filterStringParam = stringParam Expression.Assign(filterStringParam, stringParam), // Compare the parameter to the local variable Expression.Equal(stringParam, filterStringParam)); var x = Expression.Lambda>(block, stringParam).Compile();
通过使用新的参数来表示局部变量,并将其与其他参数进行比较,可以确保正确处理局部变量。这样,无论何时使用表达式树进行计算,都可以得到预期的结果。
问题的出现原因是在表达式树中捕获局部变量时,实际上是通过将局部变量"提升"为一个编译器生成的类的实例变量来实现的。C#编译器在适当的时候创建一个额外类的新实例,并将对局部变量的任何访问更改为对相关实例中实例变量的访问。
因此,表达式树需要是实例内的字段访问 - 并且实例本身是通过ConstantExpression提供的。
解决方法通常是通过在lambda表达式中创建类似的东西,然后在反编译器中查看生成的代码,将优化级别调低,以便反编译器不会将其转换回lambda表达式。
另一个解决方法是使用Expression.Property方法,例如:`var hoistedConstant = Expression.Property(Expression.Constant(new {Value = filterString}), "Value");`。
某些情况下了使用Expression.Constant方法的解决方法,例如:`Expression.Constant(filterString)`,但是这种方法不会反映变量的更改。
还某些情况下了使用lambda表达式的解决方法,例如:`var variableAccessor = ((Expression
最后,某些情况下了查看生成的MSIL代码的提示对于解决问题很有帮助。
总之,解决这个问题的方法包括使用Expression.Property方法、Expression.Constant方法和lambda表达式,并查看生成的代码来了解表达式树的创建。
(Local variable and expression trees)这个问题的出现是因为在构建用于Linq-to-entities (L2E)的表达式时,无法使用Expression.Block,因为它无法解析为SQL。
解决方法是创建一个辅助类来包含过滤器的值。具体步骤如下:
1. 创建一个辅助类ExpressionScopedVariables来包含过滤器的值。
2. 构建表达式树:
- 创建一个ExpressionScopedVariables对象scope,并将filterString的值赋给它。
- 创建一个表示scope的常量表达式filterStringExp。
- 使用反射获取ExpressionScopedVariables类中的Value属性,并创建一个成员访问表达式access。
3. 将原始代码中的常量替换为成员访问表达式access。
4. 构建最终的表达式树lambda1。
以上方法对于L2E非常有用,可以根据实际需求进行适当的调整。
这个方法帮助了我很多,非常感谢!虽然我的需求有些不同,但是创建一个"scope"类并从中创建一个表达式的概念指导了我走向正确的方向。
非常感谢!这真的很有帮助!