博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

LINQ中的lambda表达式

Posted on 2010-11-22 19:03  steve.z  阅读(1795)  评论(0编辑  收藏  举报

“=>”就是lambda表达式。以下三种方式等效,其中第二种运用了lambda表达式。 

List<Employee> l = getEmployees();

==============第一种==================
        //IEnumerable<Employee> matches = from emp in l
        //                                where emp.LastName.StartsWith("S")
        //                                orderby emp.LastName ascending
        //                                select emp;

==============第二种==================

        // IEnumerable<Employee> matches = l.Where(emp => emp.LastName.StartsWith("S")).OrderBy(emp => emp.LastName).Select(emp => emp);

==============第三种==================

        IEnumerable<Employee> matches = l.Where(delegate(Employee emp) { return emp.LastName.StartsWith("S"); }).OrderBy(delegate(Employee emp) { return emp.LastName; }).Select(delegate(Employee emp) { return emp; });

//这里需要注意的是最后调用Select()方法。


        gvList.DataSource = matches;
        gvList.DataBind();

 

Linq Lambda表达式全面分析

介绍Linq Lambda表达式之前,先看一个例子:

  1. Expression<Func<string, bool>> expr = o => o.Length > 10; 

初次接触Linq Lambda表达式的人可能会被搞迷糊,这样的语句到底是什么意思,怎么样工作,原理又是什么。

逐级分析以上语句,分为两个部分,以等号为界。

第一部分是变量类型的申明:Expression<Func<string, bool>> expr,表示expr这个变量是一个Linq Lambda表达式,这个表达式符合这样的一种委托:bool DelegateName(string obj)。

第二部分是表达式的声明o => o.Length > 10,这个“=>”是Lambda操作符,读作“转到”,必须把=>左右看成是一个整体,因为这实际是一个匿名方法,“=>”左边是方法传入参数的申明,右边是函数体,如果用常规的表示方法,可以写成如下形式:

  1. bool MethodName(string o)  
  2. {  
  3. return o.Length > 10;  

仔细观察两部分拆解以后的形式其实不难发现,第一部分的工作是定义了一个匿名的委托,而第二部分则是符合这个匿名委托的一个方法,由于这个方法没有明确给定名称,因此称为匿名方法。

那么,expr到底又是什么样的东西。有一点必须明确的是,expr表示绝对不是这个匿名方法的返回值,而是这个匿名方法中所有表达式的System.Linq.Expressions.Expression形式。也就是说,在expr中,这个函数体里所有的表达式已经被拆解成一个一个的单元,每一个单元都是一种System.Linq.Expressions.Expression的派生类。由于表达式和表达式之前存在着上下级的关系,因此所有的表达式呈现一种树状结构,称为表达式树。

一个匿名方法是如何转换为表达式树的呢?这个问题其实不用太过关心,因为C#编译器在对程序编译的时候已经将上述第二部分的内容自动转换为相应的表达式树了。上述例子中编译的结果通过Reflector反编译出来的内容如下所示:

  1. 1. ParameterExpression CS$0$0000;  
  2. 2. Expression<Func<string, bool>> expr = Expression.Lambda<Func<string
    bool
    >>(Expression.GreaterThan(Expression.Property(CS$0$0000 = Expression.
    Parameter(typeof(string), "o"), (MethodInfo) methodof(string.get_Length)), 
    Expression.Constant(10, typeof(int))), new ParameterExpression[] { CS$0$0000 });  

这串代码看起来有点糊,我把代码梳理了一下使得它更容易读,如下所示:

  1. Expression<Func<string, bool>> expr;  
  2.  
  3. // 创建表示参数的表达式。  
  4. ParameterExpression paramExpr = Expression.Parameter(typeof(string), "o");  
  5. // 获取表示System.String.Length属性的System.Reflection.PropertyInfo对象。  
  6. PropertyInfo propInfo = typeof(string).GetProperty
    ("Length", BindingFlags.Instance | BindingFlags.Public);  
  7. // 创建访问System.String.Length属性的表达式。  
  8. MemberExpression memberExpr = Expression.Property(paramExpr, propInfo);  
  9. // 创建一个表示常量10的表达式。  
  10. ConstantExpression constExpr = Expression.Constant(10, typeof(int));  
  11. // 创建表示左边大于右边的二分表达式。  
  12. BinaryExpression greaterThanExpr = Expression.GreaterThan(memberExpr, constExpr);  
  13. // 通过上述二分表达式创建一个Lambda表达式。  
  14. expr = Expression.Lambda<Func<string, bool>>(greaterThanExpr, paramExpr); 

是不是好麻烦啊?呵呵,好在这些工作已经在编译的时候完成了,不需要我们手工创建,除非你想动态创建表达式。关于如何动态创建表达式,我在这里就先不详细说明了,将在下一博里再详述。

综上所述,对待Linq Lambda表达式,最基本一个原则是不要把表达式看成了语句的运算结果,而应该看成这些语句本身,也就是把语句作为对象来处理。语句和语句之间通过表达式树来关联,而从语句转换为表达式树已由编译器自动完成,不需要人工介入。