学习自
《C#本质论》
Overview
在上一文中,我们简而又简的了解了一下,匿名方法和Lambda表达式,关于匿名方法这里暂且不表,本文我们来更加详细的了解一下Lambda表达式。
本文涉及到了大量的LInq方面的知识,如果看官你还没有接触到Linq那么本章可以略过。
语句Lambda和表达式Lambda
语句Lambda , 从语义上可以简单的理解为,一个语句块的委托。
比如说类似这样的,有多条语句组成的Lambda,被称为 语句Lambda
(x) =>{
int value =0;
for(int i =0 ; i<x ; i ++ ){
value+=1;
}
return value;
}
表达式 Lambda , 从语义上理解就是只有返回的表达式没有语句块。
比如说下面的,如果你接触过 Linq那么你一定很熟悉这种方式。
var result = persons.Where(t=>t.Name=="hello");
这么看起来没有什么特别显著的区别,请继续向下看。
表达式树(Expression tree)
语句Lambda和表达式Lambda最后的生成的结果,是完全不同的,一个语句Lambda将会生成一个委托,而一个表达式Lambda既可以生成一个委托,也可以转换为表达式树(expression tree)。 表达式树是一个对象,允许编译器对Lambda表达式的保主体进行分析。
在ORM框架中常常会见到类似这样的代码
var result = db.Persons.Where(t=> t.Name=="Hello");
假设Person是数据库中的一个表 假设表中有大概10w条数据,然后我们要再这个表中找到 Name = 'Hello' 的Person
当我们执行查询的时候,有两个解决思路
- 将数据库所有的数据都返回给客户端,然后客户端为灭一条记录创建一个对象,形成一个集合,然后我们通过定义的委托,来判断是否是我们需要的对象,如果是我们需要的对象那么拿出来,其他的对象丢弃掉。这种方式理论上是可行的,但是,从一个数据表中插叙1条或者几条记录,就要将所有的数据都吃进内存中,后果可想而知,所以这种方式可想而知。
- 第二个解决思路, 将我们“Lambda” 发送给您服务器数据库,然后让服务器数据库进行过滤操作,然后我们仅仅拿到符合我们的结果的几条数据。这种方式使我们ORM所通用的解决思路。
当然仅仅直接将我们是Lambda表达式发送给数据库,显然是不行的,所以我们要在中间做一层转换,将我们的Lambda表达式转换为描述Lambda描述的对象表达式树
,而不是一个编译好的一个匿名函数的代码。应为表达式数代表着是数据而不是编译好的代码,所我们能在运行的时候去分析Lambda,在ORM框架中就将 Where() 方法中接收到的表达式树转换为Sql查询语句
交给数据库去执行,并返回我们需要的数据。
转换如下:
NOTE: 表达式不是仅仅只能转换为sql语句,而是通过一个表达式树计算程序(evaluator) ,将表达式转换为任意的查询语言。
语句Lambda和表达式Lambda是如何区分的
不管是将Lambda转换为委托哈市表达式树,一个Lambda表达式都会在编译时进行全面的语义解析,如果是转换为委托,那么就会将这个Lambda 生成为一个方法,并创建委托。但是在使用Linq的时候明显不是这样的,编译器是如何判断一个Lambda该如何处理?生成委托,还是生成表达式树?
这里将以Where方法为例子,进行讲解。
利用Where方法查询集合:
static List<Person> personList = new List<Person>
{
new Person() { Name="1", Gender="Male", Age="12"},
new Person() { Name="2", Gender="Male", Age="12"},
new Person() { Name="3", Gender="Male", Age="12"},
};
static void Main(string[] args)
{
personList.Where(t => t.Name.Equals("1"));
}
F12转到Where方法的定义 , 我们发现他是一个IEnumerable
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
看一下反编译
在这里是生成了委托。
LINQ 中的Where方法,
static void Main(string[] args)
{
DBDataContext db = new DBDataContext();
var result = db.Roles.Where(t => t.RoleName == "TestRole");
}
是IQueryable
public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
查看一下反编译,没有生成委托,而是生成了一个 Linq 表达式。
- 当Where() 方法的调用者能够隐式转换为 IEnumerable
类型时,Lambda表达式将会生成委托 - 当Where() 方法的调用者能够隐式转换为IQueryable
类型时,Lambda表达式将会生成表达式树
结语
说实话,本文确实不太好,强烈推荐大家去看一下,原文,本文是《C#》本质论的学习笔记。