学习和使用C#已经有2个月了,在这两个月的学习中,深刻体会到,C#这门语言还真不适合编程初学者学习 ,因为它是吸取了很多其他语言,不仅是面向对象,还包括函数式语言的很多特性,导致它变成特性大爆炸的 语言。它的许多方面单独拿出来讲,就得是一本书的规模,而且还不一定让人一下子明白。
LINQ,Language INtegrated Query,语言集成查询,是其中一个非常重要的部分,有关它的功能增强, 贯穿了整个C#的发展。
先从基本的查询表达式下手。
在讲查询表达式前,我们必须明白:查询 表达式不仅仅是针对数据库,它针对的是所有数据源,因为LINQ的意图就是为所有数据源提供统一的访问方式 。因为最近的项目使用的是LINQ to SQL,所以这里只讲LINQ to SQL。
查询表达式非常像SQL语句,但 书写方式却是反过来:
var articles = from article in db.Articles where a.Name == "JIM" select article;
我们必须明白,查询表达式返回的结果是一个序列,哪怕这个序列 只有一个元素。
正因为查询表达式返回的是一个序列,才使得它的行为非常有趣。序列的基本特点就 是每次只取一个元素,这使得每个转换都处理一个数据,而且只在结果序列的第一个元素被访问的时候才会开 始执行查询表达式。像是上面的数据流是这样的:select转换会在它返回的序列中的第一元素被访问时,为该 元素调用where转换,where转换会返回数据列表中第一个元素,检查这个谓词(a.Name == "JIM") 是否匹配,再把该元素返回给select。
这就是查询表达式的延迟执行,在它被创建的时候没有处理任 何数据,只是在内存中生成了这个查询的委托。这样子是非常高效和灵活的,但并不是所有情况都适合,像是 reverse这类的操作,就要访问整个数据源。所以,我们一般在返回另一个序列的操作中使用延迟执行,如果 是返回单一值就使用即时执行。
使用查询表达式,首先就是声明数据序列的数据源:
from article in db.Articles
where子句用来进行过滤。它会进入数据流中每个元素的谓词,只有返回true 的元素才能出现在结果序列中。我们可以使用多个where子句,只有满足所有谓词的元素才能进入结果序列。
编译器会将where子句转换为带有Lambda表达式的where方法调用,像是这样:
where(article => article.Name == "JIM")
所以我们也可以直接使用Lambda表达式,它同样是返回一 个序列:
var articles = db.Articles.Where(article => article)
使用Lambda表达式能使 我们的代码的简洁度大幅上升,这也是它为什么会被引进C#中的原因之一。
select子句就是投影,相 信学过数据库的同学一定非常熟悉。它同样也会有对应的方法调用,应该说,几乎所有的LINQ to SQL操作都 有对应的方法调用(因为编译器就是讲它们转换为方法调用),所以接下来就不再讲方法调用形式了。
前面讲过,编译器会将查询表达式转换为普通C#代码的方法调用,以select为例,它并不会像我们预期的那样 ,转换为Enumerable.Select或者List<T>.Select,它就只是对代码进行转换,然后再寻找适当方法。 该方法的参数会是一个委托类型或者一个Expression<T>。为什么转换后的方法中的参数是一个Lambda 表达式呢?因为Lambda表达式可以被转换为委托实例或者表达式树,所以使用Lambda表达式就可以对应所有情 况。这种转换并不依赖与特定类型,而只依赖与方法名称和参数,也就是所谓的动态类型(Duck Typing)的编 译时形式。
因为这样,我们可以实现自己的LINQ提供器,但除非真的有特殊需要,一般我们都不需要 做到这点。
我们来看看查询表达式中两个最重要的组成:范围变量和投影表达式。
from article in db.Articles
其中,article就是范围变量,而:
select article
投影表 达式就使用了该范围变量。编译器在转换的时候,Lambda表达式的左边,参数名称就是范围变量,右边来自于 投影表达式,所以,我们决不能这样写:
from article in db.Articles select person
从上面来看,范围变量应该是隐式类型,编译器会自己推断它的具体类型。当然, 我们也可以使用显式类型的范围变量,像是下面这样:
from Article article in db.Articles select article
其中,db.Articles是一个List<Article>。
这样的情况是因为我们想 要在强类型的容器中进行查询。这样的机制能够运行的保障源于一个方法:Cast()。Cast()会强制将类型转换 为目标类型,如果无法转换则会出错。
上面的表达式会转换为以下的代 码:
list.Cast<Article>().Select(article => article);
为什么需要我们对范围变 量进行显式声明呢?因为Select方法只是IEnumerable<T>的扩展方法,而不是IEnumerable,所以我们 必须要显式的声明范围变量的类型才能调用Select方法。
以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索查询
, 方法
, 表达式
, 序列
, article
, 一个
, 正则表达式查询
查询转换
linq 查询表达式、linq表达式、linq lambda表达式、linq 表达式树、linq 正则表达式,以便于您获取更多的相关知识。