Linq系列:基础与本质(Part III)

-

前面我们对LINQ的本质,以及MS针对LINQ对3.5新增的几个类做了分析。作为本系列的第一篇:基础与本质的最后一篇,我想对Lambda表达式讲解一下。本来Lambda表达式也可以放到系列c#3.x学习中,我想还是放到LINQ系列比较好讲一些。

1 Lambda表达式例子

先来熟悉一下Lambda的一般样子,代码:

 1 x => x * 2;
 2 (x, y) => x * 10 + y;
 3 (x, y, z) => (1 / x + 10) * y + z;
 4 Func<int, int> f1;
 5 f1 = x => x * 2
 6 Func<int, int, int> f2;
 7 f2 = (x, y) => x * 10 + y; 
 8 Func<int, int, int, int> f3;
 9 f3 = (x, y, z) => (1 / x + 10) * y + z;
10 // A lambda expression with no arguments
11 Func<int> f4 = ()=> 10;
12 int r1 = f1(10);
13 int r2 = f2(5, 10);

这些是C#3.0新特性为Lambda定义的格式。

Func<int, int> f1 = x => x * 2;<=> Func<int, int> f1 = delegate(int x){return x * 2;};

我们说这两种方式可以说在某种情况下是等价的,为什么等价的呢?请看本文的第二部分的分析

2 Lambda 表达本质

Lambda 表达式可以用在任何需要使用匿名方法,或是代理的地方。我们看看下面的代码:

1 List<int> numbers = new List<int>102028401358 };
2 List<int> evenNumbers = numbers.FindAll( i => ( i % 2 ) == 0 );
3 List<int> evenNumbers1 = numbers.FindAll( delegate(  int i )
4 {
5   return ( i % 2 ) == 0;
6  } );

我们通过Reflector工具可以看到编译器会将Lambda表达式编译为标准的匿名方法。下面是在Reflector下看到的代码:

1  List<int> evenNumbers = numbers.FindAll(delegate (int i) {
2          return (i % 2== 0;
3      });
4  List<int> evenNumbers1 = numbers.FindAll(delegate (int i) {
5          return (i % 2== 0;
6      });

现在看看他们的IL:

 1 L_0060: ldftn bool LINQProject.test2::<TestLambda>b__1(int32)
 2 L_0066: newobj instance void [mscorlib]System.Predicate`1<int32>::.ctor(object, native int)
 3 L_006b: stsfld class [mscorlib]System.Predicate`1<int32> LINQProject.test2::CS$<>9__CachedAnonymousMethodDelegate3
 4 L_0070: br.s L_0072
 5 L_0072: ldsfld class [mscorlib]System.Predicate`1<int32> LINQProject.test2::CS$<>9__CachedAnonymousMethodDelegate3
 6 L_0077: callvirt instance class [mscorlib]System.Collections.Generic.List`1<!0> [mscorlib]System.Collections.Generic.List`1<int32>::FindAll(class [mscorlib]System.Predicate`1<!0>)
 7 L_007c: stloc.1 
 8 L_007d: ldloc.0 
 9 L_007e: ldsfld class [mscorlib]System.Predicate`1<int32> LINQProject.test2::CS$<>9__CachedAnonymousMethodDelegate4
10 L_0083: brtrue.s L_0098
11 L_0085: ldnull 
12 L_0086: ldftn bool LINQProject.test2::<TestLambda>b__2(int32)
13 L_008c: newobj instance void [mscorlib]System.Predicate`1<int32>::.ctor(object, native int)
14 L_0091: stsfld class [mscorlib]System.Predicate`1<int32> LINQProject.test2::CS$<>9__CachedAnonymousMethodDelegate4
15 L_0096: br.s L_0098
16 L_0098: ldsfld class [mscorlib]System.Predicate`1<int32> LINQProject.test2::CS$<>9__CachedAnonymousMethodDelegate4
17 L_009d: callvirt instance class [mscorlib]System.Collections.Generic.List`1<!0> [mscorlib]System.Collections.Generic.List`1<int32>::FindAll(class [mscorlib]System.Predicate`1<!0>)
18 L_00a2: stloc.2

 

通过对Lambda表达式的IL的分析是编译器自动生成相应的静态成员和静态方法,带来着一些的都是编译器,CLR并没有做出实质的改变,这就更加验证了上一篇文章的说明,委托,匿名方法,Lambda表达式都是一脉相承的,实现原理都是通过委托。

3 Lambda表达式的分析

MS定义Lambda表达式的标准写法为:

ArgumentsToProcess => StatementsToProcess

被编译器编译为:

inputs (delegate parameters) =>Expression to be evaluated(expression return must match delegate return value type)

其中=>为Lambda的操作符。

比如前面的代码:List<int> evenNumbers = numbers.FindAll( i => ( i % 2 ) == 0 );我们可以理解为:List<int> evenNumbers = numbers.FindAll( (i) => (( i % 2 ) == 0 ));我们也可以现实的指示输入参数的类型。当有有多行处理表达式时,需要使用大括号包起来,例如:

Code

当输入参数有多个时:

Code

当没有参数的时候:()里面可以为空。

局部变量也可以在Lambda表达式中使用,例如

Code

 

最后希望大家对CLR系列和LINQ系列提出宝贵的建议以及希望增加对什么知识点的讲解。谢谢!

待续。。。

posted on 2008-11-21 18:05  gjcn  阅读(2253)  评论(7编辑  收藏  举报

导航