(转)lambda表达式的解析(二) 常量表达式
2011-10-13 22:44
316 查看
在继续探讨如何生成Expression前先对委托和表达式关系做下简单介绍,方便更好的理解后面的内容。
.net提供的Expression涵盖的表达式范围很大,从一般的含操作符表达式到带语句的表达式都支持,比如像if, foreach, try ... catch这些都可以成为表达式的一部分,但是带语句体的表达式很难被转换成SQL语句,而expression很大程度上是为LINQ服务的,所以VS编译的时候是不支持静态编译带语句体的表达式的,所有带语句体的表达式都会被直接编译成执行代码也就是做成一个委托,这样做的好处一来是简化编写DLINQ provider的工作,二来也是能消除歧义。比如
void F(Func<int> d)
void F(Expression<Func<int>> d)
调用 F(()=> { return 0; }) 时,因为编译器默认语句体不是表达式,所以直接会找到F(Func<int>d)这个方法。当然如果是 F(()=>0) 这样调用的话编译器还是会搞不清直接报错,.net解决方法是把linq to object 和 linq to db区分开:
当IEnumeralbe来调用就是用委托,IQueryable来调用就是表达式。这样一来,从编写角度看就是IEnumeralbe可以接受带语句体和不带语句体的表达式,而IQueryable就是只接受不带语句体的表达式,也就是说在用linq查询数据库的时候,当你的表达式带语句体的时候就不会产生sql语句而是在做linq to object。
这个特性是vs的一个硬性规定,为了简单起见接下来生成的表达式也是按照这个规定,当然通过增加解析处理的代码也可以把形如 ()=>{ ... } 这种语句体形式转成真正的表达式而不是委托,不过我测试下来就算你得到了一个 ()=>{ return true; } 这么个表达式,也不能把它用到IQueryable的Where方法里,因为provider不支持这种形式的表达式,所以这样的转换意义不是很大,能想到这样处理有用的地方就是:利用表达式能被运行时编译执行这个特点把它当做编译型脚本来用。
接下来开始讲表达式转换,首先讲最简单的ConstantExpression,先来看一下语法树的图
![](http://hi.csdn.net/attachment/201107/28/729107_1311833977E0Fy.png)
非常简单的一个lambda ()=>true 它被之前修改过的Grammar解析成右边这棵树,primary_expression在解析的过程中会一直接触到。上一篇说了我们的Grammar Root被设置为expression,expression的bnf为
view plaincopy to clipboardprint?
expression.Rule = conditional_expression
| bin_op_expression
| typecast_expression
| primary_expression
| query_expression;
东西非常多,其中lambda_expression 表示的就是形如(int a) => ... 的lambda表达式, 而literal就是需要被转换成ConstantExpression的节点。
在上篇提到的ProcessExpression这个方法里,处理literal的是
view plaincopy to clipboardprint?
case "literal":
return ProcessConstantExpression(expNode);
由于是常量,转换非常简单,取到literal节点的第一个子节点,也就是true这个节点,然后直接把里面存的内容返回成ConstantExpression就行了,是不是很简单?以后要讲的表达式虽然复杂,但是大致上的思路都是一致的。
.net提供的Expression涵盖的表达式范围很大,从一般的含操作符表达式到带语句的表达式都支持,比如像if, foreach, try ... catch这些都可以成为表达式的一部分,但是带语句体的表达式很难被转换成SQL语句,而expression很大程度上是为LINQ服务的,所以VS编译的时候是不支持静态编译带语句体的表达式的,所有带语句体的表达式都会被直接编译成执行代码也就是做成一个委托,这样做的好处一来是简化编写DLINQ provider的工作,二来也是能消除歧义。比如
void F(Func<int> d)
void F(Expression<Func<int>> d)
调用 F(()=> { return 0; }) 时,因为编译器默认语句体不是表达式,所以直接会找到F(Func<int>d)这个方法。当然如果是 F(()=>0) 这样调用的话编译器还是会搞不清直接报错,.net解决方法是把linq to object 和 linq to db区分开:
IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
当IEnumeralbe来调用就是用委托,IQueryable来调用就是表达式。这样一来,从编写角度看就是IEnumeralbe可以接受带语句体和不带语句体的表达式,而IQueryable就是只接受不带语句体的表达式,也就是说在用linq查询数据库的时候,当你的表达式带语句体的时候就不会产生sql语句而是在做linq to object。
这个特性是vs的一个硬性规定,为了简单起见接下来生成的表达式也是按照这个规定,当然通过增加解析处理的代码也可以把形如 ()=>{ ... } 这种语句体形式转成真正的表达式而不是委托,不过我测试下来就算你得到了一个 ()=>{ return true; } 这么个表达式,也不能把它用到IQueryable的Where方法里,因为provider不支持这种形式的表达式,所以这样的转换意义不是很大,能想到这样处理有用的地方就是:利用表达式能被运行时编译执行这个特点把它当做编译型脚本来用。
接下来开始讲表达式转换,首先讲最简单的ConstantExpression,先来看一下语法树的图
![](http://hi.csdn.net/attachment/201107/28/729107_1311833977E0Fy.png)
非常简单的一个lambda ()=>true 它被之前修改过的Grammar解析成右边这棵树,primary_expression在解析的过程中会一直接触到。上一篇说了我们的Grammar Root被设置为expression,expression的bnf为
view plaincopy to clipboardprint?
expression.Rule = conditional_expression
| bin_op_expression
| typecast_expression
| primary_expression
| query_expression;
view plaincopy to clipboardprint? primary_expression.Rule = literal | unary_operator + primary_expression | parenthesized_expression | member_access | pre_incr_decr_expression | post_incr_decr_expression | object_creation_expression | anonymous_type_creation_expression | typeof_expression | checked_expression | unchecked_expression | default_value_expression | anonymous_method_expression | lambda_expression; primary_expression.Rule = literal | unary_operator + primary_expression | parenthesized_expression | member_access | pre_incr_decr_expression | post_incr_decr_expression | object_creation_expression | anonymous_type_creation_expression | typeof_expression | checked_expression | unchecked_expression | default_value_expression | anonymous_method_expression | lambda_expression;
东西非常多,其中lambda_expression 表示的就是形如(int a) => ... 的lambda表达式, 而literal就是需要被转换成ConstantExpression的节点。
在上篇提到的ProcessExpression这个方法里,处理literal的是
view plaincopy to clipboardprint?
case "literal":
return ProcessConstantExpression(expNode);
view plaincopy to clipboardprint? private Expression ProcessConstantExpression(ParseTreeNode expNode) { var constant = expNode.FirstChild; return Expression.Constant(constant.GetObject()); } private Expression ProcessConstantExpression(ParseTreeNode expNode) { var constant = expNode.FirstChild; return Expression.Constant(constant.GetObject()); }
由于是常量,转换非常简单,取到literal节点的第一个子节点,也就是true这个节点,然后直接把里面存的内容返回成ConstantExpression就行了,是不是很简单?以后要讲的表达式虽然复杂,但是大致上的思路都是一致的。
相关文章推荐
- lambda表达式的解析(二) 常量表达式
- (转)lambda表达式的解析(四) 运算符表达式
- JDK 8 Lambda 表达式解析Map和List
- 将Lambda表达式作为参数传递并解析——在构造函数参数列表中使用Lambda表达式(C#)
- lambda 表达式解析
- C++11 lambda 表达式解析
- C++11 lambda 表达式解析
- Python中 Lambda表达式全面解析
- C++11 lambda 表达式解析
- (转)lambda表达式的解析(五) Lambda表达式与闭包类型
- C++11 lambda 表达式解析
- CYQ.Data 数据框架 V5 的语法糖 外置开源原理解析 [类似lambda表达式]
- 深入解析Python中的lambda表达式的用法
- (转)lambda表达式的解析(六) 成员访问表达式
- C++11 lambda 表达式解析
- JAVA8 Lambda表达式完全解析
- C++11 lambda 表达式解析
- C++11 lambda 表达式解析
- (转)lambda表达式的解析(七) 对象的创建
- C++11 lambda 表达式解析