20.3 Lambda表达式

  Lambda表达式是一种高效的类似于函数式编程的表达式,Lambda简化了开发中需要编写的代码量。Lambda表达式是由.NET 2.0演化过来的,也是LINQ的基础,熟练的掌握Lambda表达式能够快速的上手LINQ应用开发。

20.3.1 匿名方法
  在了解Lambda表达式之前,需要了解什么是匿名方法,匿名方法简单的说就是没有名字的方法,而通常情况下的方法定义是需要名字的,示例代码如下所示。
+展开
-C#
        public int sum(int a, int b)//创建方法
        {
            return a + b;//返回值
        }

  上面这个方法就是一个常规方法,这个方法需要方法修饰符(public)、返回类型(int)方法名称(sum)和参数列表。而匿名方法可以看作是一个委托的扩展,是一个没有命名的方法,示例代码如下所示。
+展开
-C#
        delegate int Sum(int a,int b);//声明匿名方法
        protected void Page_Load(object sender, EventArgs e)
        {
            Sum s = delegate(int a,int b)//使用匿名方法
            {
                return a + b;//返回值
            };
        }

  上述代码声明了一个匿名方法Sum但是没有实现匿名方法的操作的实现,在声明匿名方法对象时,可以通过参数格式创建一个匿名方法。匿名方法能够通过传递的参数进行一系列操作,示例代码如下所示。
+展开
-C#
Response.Write(s(5,6).ToString());

  上述代码使用了s(5,6)方法进行两个数的加减,匿名方法虽然没有名称,但是同样可以使用“(”“)”号进行方法的使用。
  注意:虽然匿名方法没有名称,但是编译器在编译过程中,还是会为该方法定义一个名称,只是在开发过程中这个名称是不被开发人员所看见的。
除此之外,匿名方法还能够使用一个现有的方法作为其方法的委托,示例代码如下所示。
+展开
-C#
        delegate int Sum(int a,int b);//方法委托
        public int retSum(int a, int b)//普通方法
        {
            return a + b;
        }

  上述代码声明了一个匿名方法,并声明了一个普通的方法,在代码中使用匿名方法代码如下所示。
+展开
-C#
        protected void Page_Load(object sender, EventArgs e)
        {
            Sum s = retSum;//使用匿名方法
            int result = s(10, 10);
        }

  从上述代码中可以看出,匿名方法是一个没有名称的方法,但是匿名方法可以将方法名作为参数进行传递,如上述代码中变量s就是一个匿名方法,这个匿名方法的方法体被声明为retSum,当编译器进行编译时,匿名方法会使用retSum执行其方法体并进行运算。匿名方法最明显的好处就是可以降低常规方法编写时的工作量,另外一个好处就是可以访问调用者的变量,降低传参数的复杂度。

20.3.2 Lambda表达式基础
  在了解了匿名方法后,就能够开始了解Lambda表达式,Lambda表达式在一定程度上就是匿名方法的另一种表现形式。为了方便对Lambda表达式的解释,首先需要创建一个People类,示例代码如下所示。
+展开
-C#
    public class People
    {
        public int age { getset; }//设置属性
        public string name { getset; }//设置属性
        public People(int age,string name)//设置属性(构造函数构造)
        {
            this.age = age;//初始化属性值age
            this.name = name;//初始化属性值name
        }
    }

  上述代码定义了一个People类,并包含一个默认的构造函数能够为People对象进行年龄和名字的定义。在应用程序设计中,很多情况下需要创建对象的集合,创建对象的集合有利于对对象进行操作和排序等操作,以便在集合中筛选相应的对象。使用List进行泛型编程,可以创建一个对象的集合,示例代码如下所示。
+展开
-C#
            List<People> people = new List<People>();//创建泛型对象
            People p1 = new People(21,"guojing");//创建一个对象
            People p2 = new People(21, "wujunmin");//创建一个对象
            People p3 = new People(20, "muqing"); //创建一个对象
            People p4 = new People(23, "lupan"); //创建一个对象
            people.Add(p1);//添加一个对象
            people.Add(p2);//添加一个对象
            people.Add(p3);//添加一个对象
            people.Add(p4);//添加一个对象

  上述代码创建了4个对象,这4个对象分别初始化了年龄和名字,并添加到List列表中。当应用程序需要对列表中的对象进行筛选时,例如需要筛选年龄大于20岁的人时,就需要从列表中筛选,示例代码如下所示。
+展开
-C#
IEnumerable<People> results = people.Where(delegate(People p) { return p.age > 20; });//匿名方法

  上述代码通过使用IEnumerable接口创建了一个result集合,并且该集合中填充的是年龄大于20的People对象。细心的读者就能够发现在这里使用了一个匿名方法进行筛选,因为该方法没有名称,该匿名方法通过使用People类对象的age字段进行筛选。
  虽然上述代码中执行了筛选操作,但是使用匿名方法往往不太容易理解和阅读,而Lambda表达式相比于匿名方法而言更加容易理解和阅读,示例代码如下所示。
+展开
-C#
IEnumerable<People> results = people.Where(People => People.age > 20);//Lambda

  上述代码同样返回了一个People对象的集合给变量results,但是其编写的方法更加容易阅读,这里可以看出Lambda表达式在编写的格式上和匿名方法非常相似。其实当编译器开始编译并运行,Lambda表达式最终也表现为匿名方法。

  使用匿名方法实际上并不是创建了没有名称的方法,实际上编译器会创建一个方法,这个方法对于开发人员来说是看不见的,该方法会将People类的对象中符合p.age>20条件的对象返回并填充到集合中。相同的是,使用Lambda表达式,当编译器编译时,Lambda表达式同样会被编译成一个匿名方法进行相应的操作,但是相比于匿名方法而言,Lambda表达式更容易阅读,Lambda表达式的格式如下所示。
   (参数列表)=>表达式或者语句块
  如上述代码中,参数列表就是People类,表达式和语句块就是People.age>20,使用Lambda表达式能够让人很容易的理解该语句究竟是如何执行的,虽然匿名方法提供了同样的功能,却并不容易理解。相比之下People => People.age > 20却能够很好的理解为“返回一个年纪大于20的人”。其实Lambda表达式并没有什么高深的技术,Lambda表达式可以看作是匿名方法的另一种表现形式。其实Lambda表达式经过反编译后,与匿名方法并没有什么区别。

20.3.3 Lambda表达式格式
  Lambda表达式是匿名方法的另一种表现形式。比较Lambda表达式和匿名方法,在匿名方法中,“(”,“)”内是方法的参数的集合,这就对应了Lambda表达式中“(参数列表)”,而匿名方法中“{”,“}”内是方法的语句块,这也对应了Lambda表达式“=>”符号右边的表达式和语句块项。由于Lambda表达式是一种匿名方法,所以Lambda表达式也包含一些基本格式,这些基本格式如下所示。
  Lambda表达式可以有多个参数,一个参数,或者无参数。其参数类型可以隐式或者显式。示例代码如下所示:
+展开
-C#
(x, y) => x * y //多参数,隐式类型=> 表达式
x => x * 5//单参数, 隐式类型=>表达式
x => { return x * 5; }//单参数,隐式类型=>语句块
(int x) => x * 5//单参数,显式类型=>表达式
(int x) => { return x * 5; }//单参数,显式类型=>语句块
() => Console.WriteLine()//无参数

  上述格式都是Lambda表达式的合法格式,在编写Lambda表达式时,可以忽略参数的类型,因为编译器能够根据上下文直接推断参数的类型,示例代码如下所示。
+展开
-C#
(x, y) => x + y //多参数,隐式类型=> 表达式

  Lambda表达式的主体可以是表达式也可以是语句块,这样就节约了代码的编写。
  注意:Lambda表达式与匿名方法的另一个不同是,Lambda表达式的主体可以是表达式也可以是语句块,而匿名方法中不能包含表达式。
  Lambda表达式中的表达式和表达式体都能够被转换成表达式树,这在表达式树的构造上会起到很好的作用,表达式树也是LINQ中最基本最重要的概念。

20.3.4 Lambda表达式树
  Lambda表达式树也是LINQ中最重要的一个概念,Lambda表达式树允许开发人员像处理数据一样对Lambda表达式进行修改。理解Lambda表达式树的概念并不困难,Lambda表达式树就是将Lambda表达式转换成树状结构,在使用Lambda表达式树之前还需要使用System.Linq.Expressions命名空间,示例代码如下所示。
+展开
-C#
using System.Linq.Expressions;//使用命名空间

  Lambda表达式树的基本形式有两种,这两种形式代码如下所示。
+展开
-C#
Func<intint> func = pra => pra * pra;//创建表达式树
Expression<Func<intint>> expression = pra => pra * pra;//创建表达式树

  Lambda表达式树就是将Lambda表达式转换成树状结构,示例代码如下所示。
+展开
-C#
Func<intint> func = (pra => pra * pra); //创建表达式
Response.Write(func(8).ToString());//执行表达式
Response.Write("<hr/>");

  上述代码直接用Lambda表达式初始化Func委托,运行后返回的结果为64,同样使用Expression类也可以实现相同的效果,示例代码如下所示。
+展开
-C#
Expression<Func<intint>> expression = pra => pra * pra;//创建表达式
Func<intint> func1 = expression.Compile();//编译表达式
Response.Write(func1(8).ToString());

  上述代码运行后同样返回64,运行后如图20-10所示。使用Func类和Expression类创建Lambda表达式运行结果基本相同,但是Func方法和Expression方法是有区别的,如Lambda表达式pra => pra * pra,Expression首先会分析该表达式并将表达式转换成树状结构,如图20-11所示。
Lambda表达式树
图20-10 Lambda表达式树
Lambda表达式树格式
图20-11 Lambda表达式树格式
  当编译器编译Lambda表达式时,如果Lambda表达式使用的是Func方法,则编译器会将Lambda表达式直接编译成匿名方法,而如果Lambda表达式使用的是Expression方法,则编译器会将Lambda表达式进行分析、处理然后得到一种数据结构。

20.3.5 访问Lambda表达式树
  既然在LINQ应用开发中常常需要解析Lambda表达式,则就不能避免的对Lambda表达式树进行访问,访问Lambda表达式的方法非常简单,直接将表达式输出即可,示例代码如下所示。
+展开
-C#
            Expression<Func<intint>> expression = pra => pra * pra;//创建表达式树
            Func<intint> func1 = expression.Compile();//表达式树编译
            Response.Write(func1(8).ToString());//执行表达式
            Response.Write(expression.ToString());//执行表达式

  上述代码直接使用Expression类的对象进行表达式输出,这时候读者可能会想到,是否能够像Expression类的对象一样直接将Func对象进行输出,答案是否定的,而如果直接使用Func对象是不能够输出表达式的,如图20-12所示。
Lambda表达式输出
图20-12 Lambda表达式输出
  正如图20-12所示,直接输出Expression类的对象的值能够进行表达式的输出,而如果输出Func类的对象只会输出系统信息,如System.Func`2[System.Int32,System.Int32],这并不是期待的结果。这也从另一个角度说明了Func方法和Expression方法是有区别的。

加支付宝好友偷能量挖...


评论(0)网络
阅读(100)喜欢(0)asp.net-linq