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

委托、匿名方法、Lambda表达式的演进

Posted on 2014-05-19 19:05  米粒3  阅读(182)  评论(0编辑  收藏  举报

假设给我们一个泛型对象List<T>,T为int类型,要求我们使用该对象方法FindAll(Predicate<T> match)从中找出该List中的偶数,您如何实现?

  说明一下:Predicate<T>是一个泛型委托,它的原型为public delegate bool Predicate<T>(T obj),该委托传入一个T类型对象,经逻辑判断后返回布尔值。

委托

  可能您首先想到的是用委托实现,实现方法如下:

复制代码
//方法1
staticvoid Method1()
{
//创建List<int>对象
List<int> list =new List<int>();
list.AddRange(
newint[] { 1, 2, 3, 4, 5, 6 });

//定义一个委托
Predicate<int> callback =new Predicate<int>(IsEvenNumber);
//执行FindAll方法,该方法使用传入的委托callback来判断列表中的整数是否为偶数
List<int> evenNumbers = list.FindAll(callback);

//输出
foreach (int i in evenNumbers)
{
Console.Write(
"{0: 0}", i);
}

Console.WriteLine();
}

//判断传入的整数是否为偶数
staticbool IsEvenNumber(int i)
{
return i %2==0;
}
复制代码

  这个方法不错,简单、直观,就是有些......,有些什么?代码显得有些累赘,我们仅仅是要执行“i % 2 == 0”这样一个判断语句,却要为这一条语句专门定义一个方法和一个委托。如果该方法在别处根本未被使用,那有没有什么办法省去它呢(当然不是真正的省去,编 译后,该方法其实还存在,我们的目的是至少不用让咱们特意为这条语句输入一个方法和一个委托)?这时,我们的匿名方法该登场啦!

匿名方法

  看下面的代码:

复制代码
staticvoid Method2()
{
//创建List<int>对象
List<int> list =new List<int>();
list.AddRange(
newint[] { 1, 2, 3, 4, 5, 6 });

//创建匿名方法,并将其传给FindAll方法
List<int> evenNumbers = list.FindAll
                          (
                            
delegate(int i)
                            {
                              
return i %2==0
;
                            }

                          );

//输出
foreach (int i in evenNumbers)
{
Console.Write(
"{0: 0}", i);
}

Console.WriteLine();
}
复制代码

  上例中delegate(int i) { return i %2==0; }语句创建了一个内联匿名方法的委托,并传给FindAll用于判断列表中的一个整数是否为偶数。相较于Method1,我们不用特意定义一个方法和一个委托,代码是精减了不少,但看着是不是有些别扭?这个匿名方法的原型是啥样?

  么急,一点点来分析。(int i)就是咱们定义的匿名方法的参数列表,return后返回一个布尔值,这就是该方法的返回值,这么看来,咱们的内联匿名方法的委托原型其实就是:

  delegate bool 匿名(int i)

  看起来眼熟啊!这不和Predicate<T>的原型public delegate bool Predicate<T>(T obj),T代入int类型后一样嘛!耶,真巧啊!是啊,不巧不行啊,您定义的匿名方法原型如果不是这个样的话,那编译器就直接报错 啦!List<int>.FindAll方法可是要求传入一个Predicate<T>的委托呢!

  嗯,这么说来,内联匿名方法的委托定义语法是这个样啦:

delegate(参数列表)
{
执行语句
}

Lambda表达式

  匿名方法的实现已经够简练了,还有啥更简练的语法来实现么?啥?还要简练?匿名方法的语法就够晕乎了,还要咋个简练法,有些BT吧!么办法,微软就是这么BT。咱们就来看看更BT的Lambda表达式吧。小二,上代码:

复制代码
staticvoid Method3()
{
//创建List<int>对象
List<int> list =new List<int>();
list.AddRange(
newint[] { 1, 2, 3, 4, 5, 6 });

//创建Lambda表达式并传给FindAll方法
List<int> evenNumbers = list.FindAll
(
(
int x) => { return x %2==0; } //注意这里噢,这就是大名鼎鼎的Lambda表达式
);

//输出
foreach (int i in evenNumbers)
{
Console.Write(
"{0: 0}", i);
}

Console.WriteLine();
}
复制代码

  看到这行代码了吧:(int x) => { return x % 2 == 0; }

  这是个啥东西?Lambda表达式?对头!

  让我们来分析一下Lambda表达式的语法:别的先不管,关注一下“=>”符号。Lambda表达式被该符号分为两部分,符号的前半部分是参数列表,后半部分是执行语句,简化一下就是这样:

  参数列表=>执行语句

  眼熟不?嗯,好像、大概、似乎、肯定、因为、所以有点眼熟,对了,这结构不和匿名方法一个样吗?咱再回头看看匿名方法的语法:

  delegate(参数列表){执行语句}

  看出来了吧,Lambda表达式就是匿名方法嘛!区别仅在于用“=>”符号隔开了参数列表和执行语句而已。这么说来,语句(int x) => { return x % 2 == 0; }不过是为FindAll传入了一个内联匿名方法的委托,该方法的参数列表为(int x),返回一个布尔值。

  至此,是不是值得庆祝一下?我们好像开始理解这个BT的Lambda表达式了,现在应该叫小二上二锅头了吧,我再也不想看这让人晕乎的代码了。不过,先别高兴,微软又说了,这还不是最简练的!啥?这还不简练?那你写一个更简练的看看?小二,再上一盘代码:   

 

复制代码
staticvoid Method4()
{
//创建List<int>对象
List<int> list =new List<int>();
list.AddRange(
newint[] { 1, 2, 3, 4, 5, 6 });

//创建Lambda表达式并传给FindAll方法
List<int> evenNumbers = list.FindAll
(
x
=> x %2==0//看到这里,是不是有些无语了
);

//输出
foreach (int i in evenNumbers)
{
Console.Write(
"{0: 0}", i);
}

Console.WriteLine();
}
复制代码

  看看这个:

  x=> x % 2 == 0

  这是我们第二次看到Lambda表达式了,只要记着Lambda表达式的语法:(参数列表=>执行语句) 就行了。x就是该式的参数列表,为啥x连个参数类型都没有呢?因为编译器知道咱们要传入的方法原型是bool Predicate<int>(int obj),自动就认为参数x的类型为int类型。同理,编译器也知道方法的返回值为布尔型,执行语句(x % 2 == 0)就是一个布尔型表达式,所以,编译器也认为咱们要返回的就是这个表达式了。最后Lambda表达式就像被扒光了衣服的小正太一样,裸奔在咱们的代码中 了。嗯,很好,裸一裸更健康!

  咱们的委托、匿名方法、Lambda表达式的演进之旅这样结束了。不过,好像还有一个问题,如果咱们的匿名方法不带参数,那Lambda应该怎么写?嗯,先别说答案,让我想想,是不是这样“()=>{执行语句}”?对啦,就是这样!

  刚接触这种BT的Lambda表达式,真是有些讨厌,这语法简练到“祼奔”了,但时间长了,你会发现它实在是太强大了,你一定会爱上这BT的Lambda表达式的。