16.C#初见Lambda表达式及表达式树(九章9.1-9.3)

  在说明Lambda相关知识前,我们需要了解Lambda表达式常用于LINQ,那么我们来聊下LINQ。

  LINQ的基本功能就是创建操作管道,以及这些操作需要的任何状态。这些操作表示了各种关于数据的逻辑:如何过滤、如何排序以及如何将不同的数据源连接在一起,等等。执行委托只是LINQ的众多能力之一。为了富有效率地使用数据库和其他查询引擎,我们需要以一种不同的方式来表示管道中的各个操作。这种不同的方式就可以使用Lambda表达式来表现。下面分别使用委托(使用匿名函数)和Lambda表达式来作出同样的事情:返回一个人的到现在一共活了多少天。

 1 class Person
 2 {
 3     public DateTime BirthDay { get; set; }
 4 }
 5
 6 public delegate int GetLifeDays(Person p);//声明一个委托类型
 7 static void Main(string[] args)
 8 {
 9     Person p = new Person() { BirthDay = new DateTime(1900, 12, 17) };
10
11     GetLifeDays gfd = delegate (Person x) { //实例化一个委托12         return (DateTime.Now - x.BirthDay).Days;
13     };
14     Console.WriteLine(gfd(p));
15
16     GetLifeDays gfd1 = (Person x) => { return (DateTime.Now - x.BirthDay).Days; };
17     Console.WriteLine(gfd1(p));
18
19     GetLifeDays gfd2 = (Person x) => (DateTime.Now - x.BirthDay).Days;  //去除了后面的大括号,“;”为表达式结束,不是Lambda的结束
20     Console.WriteLine(gfd2(p));
21
22     GetLifeDays gfd3 = (x) => { return (DateTime.Now - x.BirthDay).Days; }; //让编译器推断参数的类型
23     Console.WriteLine(gfd3(p));
24
25     GetLifeDays gfd4 = (x) =>  (DateTime.Now - x.BirthDay).Days; //同时省去参数类型和大括号
26     Console.WriteLine(gfd4(p));
27
28     GetLifeDays gfd5 = x => (DateTime.Now - x.BirthDay).Days; //再进一步,省去参数列表的括号
29     Console.WriteLine(gfd5(p));
30
31     Console.ReadKey();
32 }

  上述是单一参数的各种情况,对于有两个或多个参数的,效果是"一个到到从出生到某一天的相隔天数",某一天肯定是要大于出生那天啦。

 1 public delegate int GetDaysTo(Person p, DateTime d);
 2 static void Main(string[] args)
 3 {
 4     Person p = new Person() { BirthDay = new DateTime(1900, 12, 17) };
 5
 6     DateTime d = new DateTime(2100, 12, 12);
 7     //使用匿名方法
 8     GetDaysTo gdt = delegate (Person x, DateTime y)
 9     {
10         return (y - x.BirthDay).Days;
11     };
12     Console.WriteLine(gdt(p, d));
13
14     GetDaysTo gdt1 = (Person x, DateTime y) => { return (y - x.BirthDay).Days; };
15     Console.WriteLine(gdt1(p, d));
16
17     GetDaysTo gdt2 = (Person x, DateTime y) => (y - x.BirthDay).Days;
18     Console.WriteLine(gdt2(p, d));
19
20     GetDaysTo gdt3 = (x, y) => (y - x.BirthDay).Days;
21     Console.WriteLine(gdt3(p, d));
22
23     //GetDaysTo gdt4 = x, y => (y - x.BirthDay).Days; Error
24     //Console.WriteLine(gdt4(p, d));
25
26     Console.ReadKey();
27 }

  可以看出当参数为两个或两个以上时,不能省略参数列表中的括号,那也可以想像在语句两条或两条以上时,不能省略大括号。

  下面结合之前的知识,对一个列表使用ambda表达式进行操作。

 1 //使用集合初始化器
 2 List<Person> l = new List<Person> {
 3     new Person { BirthDay=new DateTime(1990,11,11)},
 4     new Person { BirthDay=new DateTime(1890,12,12)},
 5     new Person { BirthDay=new DateTime(1891,12,12)},
 6     new Person { BirthDay=new DateTime(1892,12,12)},
 7     new Person() { BirthDay=new DateTime(1870,12,12)}
 8 };
 9
10 //找到大于new DateTime(1890,1,1)的人
11 var result0 = l.FindAll(x => x.BirthDay > new DateTime(1890, 1, 1));
12
13 //按年龄从小到大排序
14 l.Sort((x, y) => x.BirthDay > y.BirthDay ? -1 : 1);
15 foreach (var e in l)
16 {
17     Console.WriteLine((DateTime.Now - e.BirthDay).Days);
18 }
19
20 //循环打印每个人的出生天数,效果和上面的foreach一样
21 l.ForEach(x => Console.WriteLine((DateTime.Now - x.BirthDay).Days));
22
23 //找到BirthDay=new DateTime(1890,12,12)的人
24 var result1 = l.Find(x => x.BirthDay == new DateTime(1890, 12, 12));

  接下来,我们来说下表达式树,.NET3.5的表达式提供了一种抽象的方式将一些代码表示成一个对象树,表达式树主要用于LINQ。System.Linq.Expressions命名空间包含了代表表达式的各个类,它们都继承于Expression,一个抽象的主要包含一些静态工厂方法的类,这些方法用于创建其它表达类的实例。

  Expression类包含两个属性:

  1. Type属性代表表达式求值后.NET类型,可把它视为一个返回类型。例如,一个表达式要获取一个字符串的长度,则该表达式的类型为int。
  2. NodeType属性返回所代表的表达式的种类。它是ExpressionType枚举的成员。
1 Expression first = Expression.Constant(5);
2 Expression result = Expression.Add(first, first);
3 Console.WriteLine(result);

  断点对象各属性

  上图分别为first对象和result对象的各属性值。

  • 将表达式树编译成委托

  LambdaExpression是从Expression派生的类型之一。泛型类Expression<TDelegate>又是从LambdaExpression中派生。Expression和Expression<TDelegate>区别在于泛型类以静态类的方法标识了它是什么各类的表达式,也就是说,它确定了返回类型和参数。很显然,这是用TDelegate类型参数来表示的,它必须是一个委托类型。LambdaExpression有一个Compile方法能创建恰当类型的委托。Expression<TDelegate>也有一个同名的方法 ,但它静态类型化返回TDelegate类型的委托。如:

1 Expression first = Expression.Constant(5);
2 Expression result = Expression.Add(first, first);
3 Func<int> add = Expression.Lambda<Func<int>>(result).Compile();
4 Console.WriteLine(add()); //10
  • 将C#Lambda表达式转换为表达式树

  Lambda表达式能显式或隐式地转换为恰当的委托实例,然而这些并非唯一能进行的转换,还可以要求编译器通过你的Lambda表达式构建一个表达式树,在执行时创建Expression<TDelegate>的一个实例。如

1 Expression<Func<int>> re = () => 5;
2 Func<int> add0 = re.Compile();
3 Console.WriteLine(add0());

  后面的那些内容真心太复杂了,自己实在理解不了,而且日常使用中也没有使用过,没有底气聊这个话题,想深入的朋友可以自己深入去了解,这里就做罢了吧。

  请斧正。

时间: 2024-12-13 14:13:10

16.C#初见Lambda表达式及表达式树(九章9.1-9.3)的相关文章

Lambda表达式和表达式树

在C# 2.0中,通过方法组转换和匿名方法,使委托的实现得到了极大的简化.但是,匿名方法仍然有些臃肿,而且当代码中充满了匿名方法的时候,可读性可能就会受到影响.C# 3.0中出现的Lambda表达式在不牺牲可读性的前提下,进一步简化了委托. LINQ的基本功能就是创建操作管道,以及这些操作需要的任何状态.这些操作表示了各种关于数据的逻辑,例如数据筛选,数据排序等等.通常这些操作都是用委托来表示.Lambda表达式是对LINQ数据操作的一种符合语言习惯的表示方式. Lambda表达式不仅可以用来创

C#中的Lambda表达式和表达式树

在C# 2.0中,通过方法组转换和匿名方法,使委托的实现得到了极大的简化.但是,匿名方法仍然有些臃肿,而且当代码中充满了匿名方法的时候,可读性可能就会受到影响.C# 3.0中出现的Lambda表达式在不牺牲可读性的前提下,进一步简化了委托. LINQ的基本功能就是创建操作管道,以及这些操作需要的任何状态.这些操作表示了各种关于数据的逻辑,例如数据筛选,数据排序等等.通常这些操作都是用委托来表示.Lambda表达式是对LINQ数据操作的一种符合语言习惯的表示方式. Lambda表达式不仅可以用来创

c# Lambda表达式和表达式树

using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Linq; using System.Text; using System.Threading.Tasks; namespace SRLJCSHAP.委托.Demo { delegate void StringProcessor(string input);//声明委托类型 public class Person {

C#复习笔记(4)--C#3:革新写代码的方式(Lambda表达式和表达式树)

Lambda表达式和表达式树 先放一张委托转换的进化图 看一看到lambda简化了委托的使用. lambda可以隐式的转换成委托或者表达式树.转换成委托的话如下面的代码: Func<string, int> getLength = s => s.Length; 转换成表达式树的话是下面的代码: Expression<Func<string, int>> getLength = s => s.Length; 委托方面的东西前面都做了详细的介绍.我们主要学习表达

C# 表达式树 创建、生成、使用、lambda转成表达式树~表达式树的知识详解

笔者最近学了表达式树这一部分内容,为了加深理解,写文章巩固知识,如有错误,请评论指出~ 表达式树的概念 表达式树的创建有 Lambda法 和 组装法. 学习表达式树需要 委托.Lambda.Func<> 基础. 表达式树 形状可以参考 二叉树. 可以把表达式树理解成 数学表达式. 数学表达式的所有常量.符号为表达式树的底节点.每一次计算生成的结果是一个结点,或者说他们的共同结点就是他们应该进行的运算. 生成表达式树 表达式树的创建有 Lambda表达式法 和 组装法 为了方便,这里指定生成的表

[.net 面向对象程序设计进阶] (5) Lamda表达式(二) 表达式树快速入门

[.net 面向对象程序设计进阶] (6) Lamda表达式(二) 表达式树快速入门 本节导读: 认识表达式树(Expression Tree),学习使用Lambda创建表达式树,解析表达式树. 学习表达式在程序设计中的优点:比如构造动态查询.动态构造表达式树完成未知对象属性访问,比反射的性能高出很多.我们可以说表达式树才是Lambda的精髓,是我们必须要熟练掌握并灵活运用的. 1.关于表达式树(Expression Tree) 表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如

[.net 面向对象程序设计进阶] (7) Lamda表达式(三) 表达式树高级应用

[.net 面向对象程序设计进阶] (7) Lamda表达式(三) 表达式树高级应用 本节导读:讨论了表达式树的定义和解析之后,我们知道了表达式树就是并非可执行代码,而是将表达式对象化后的数据结构.是时候来引用他解决问题.而本节主要目的就是使用表达式树解决实际问题. 读前必备: 本节学习前,需要掌握以下知识: A.继承 [.net 面向对象编程基础]  (12) 面向对象三大特性——继承 B.多态 [.net 面向对象编程基础]  (13) 面向对象三大特性——多态 C.抽象类 [.net 面向

前中后缀表达式以及表达式树

中缀表达式就是我们平时喜闻乐见的形式:二元运算符在中间,它的两个操作数在两侧: a + b * c + ( d * e + f ) * g 后缀和前缀表达式,顾名思义就是把运算符分别放在前面或者后面,注意没有括号,手工转换方法是按运算顺序添加括号,然后把相应的运算符置于相应的括号的前或后,如: ((a + ( b * c)) + (((d * e) + f) * g)) 放到前面有: +(+(a*(b c))*(+((*(d e))f)g)) 放到后面有: ((a(b c)*)+(((d e)*

ACM:树的变换,根据表达式建立表达式树

题目:输入一个表达式,建立一个表达式树! 分析:找到最后计算的运算符(它是整棵表达式树的根),然后递归处理! 在代码中,只有当p==0的时候,才考虑这个运算符,因为括号里的运算符一定不是最后计算的,应当忽略! 由于加减跟乘除都是左结合的,最后一个运算符才是最后计算的,所以用两个变量c1跟c2分别记录在括号外面的"最右"出现的加减号和乘除号. #include <iostream> #include <string> using namespace std; co