摘要:
本文旨在对LINQ新手提供一篇入门性的,同时易于随时查阅和参考的入门性文章主要参考资料是C# 4.0 in a Nutshell 英文版,所以中文翻译可能和一般的不大一样。
概述:
LINQ(Language INtegrated Query) 是在.NET3.5之后引入的一项能够对本地或者远程的数据集合进行结构化的类型安全的查询的特性, 对于入门者来说,就是通过有点像SQL那样的方法来操作数据集合.
LINQ可以查询的集合必须实现IEnumberable<T>接口,它同时提供了编译时类型检查和动态查询的好处。LINQ主要的类型都定义在System.Linq 和 System.Linq.Expressions NS里,此外,对于LINQ to XML,引用System.Xml.Linq NS
一个简单的LINQ查询示例
///本演示中被查询的对象 string[] names = { "Jim", "Steve", "Bill"}; ///查询(query) 是一个通过查询操作符来把一个输入的序列转换成一个输出的序列,对于上面这个例子,我们要选择出长度小于4的名字的查询只需要一个Where操作符 IEnumerable<string> shortName = System.Linq.Enumerable.Where(names, n => n.Length < 4); ///这一句可以用下面这句等价替换 IEnumerable<string> shortName = names.Where(n => n.Length < 4); ///这是因为所有标准的Linq查询操作符都是以extension method实现的, ///Extension Method是在C# 3.0 中提供的一个种可以通过类实例来调用静态方法的机制 ///再次,这一句还可以通过使用var来进一步简化代码 var shortName = names.Where(n => n.Length < 4); ///这一次,我们是否还可以写的再简单一点呢? 其实是可以的,通过连续使用查询操作符,我们可以再次简化成这样 var shortName = from nm in names where nm.Length < 4 select nm; foreach (string str in shortName) { Console.WriteLine(str); } ///这个输出的结果也就是一个Jim了。 对于这种连续的查询操作符使用, ///那么,第一部分的这个简单的demo就到这里。接下来按照书上的说法我们分别探讨LINQ的基本组成部分
语法基础
Fluent Syntax
(其实我不知道中文翻译是什么)允许开发者使用连锁的查询操作符来进行复杂的查询,本小节的例子初始版如下:
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" }; IEnumerable<string> query = names .Where (n => n.Contains ("a")) .OrderBy (n => n.Length) .Select (n => n.ToUpper()); foreach (string name in query) Console.WriteLine (name); ///在这句话里面,Where最先过滤出了所需的集合,即包含a的名字, ///OrderBy对Where过滤出的集合进行排序再返回 ///Select对OrderBy的数据结果通过select的lambda表达式处理,这里就是大写 ///上例等价于一下几句 IEnumerable<string> filtered = names .Where (n => n.Contains ("a")); IEnumerable<string> sorted = filtered.OrderBy (n => n.Length); IEnumerable<string> finalQuery = sorted .Select (n => n.ToUpper());
从这个例子中可以看出,Fluent Syntax的执行是按顺序一步一步来的,上面的例子之所以能够简化为这么精简的写法,全部都应该归功于extension methods, 没有它,那个句子就悲剧般的变成了这样:
IEnumerable<string> query = Enumerable.Select ( Enumerable.OrderBy ( Enumerable.Where ( names, n => n.Contains ("a") ), n => n.Length ), n => n.ToUpper() );
Lambda表达式在LINQ中的作用
LINQ的基本语法就是这样,于是但我们真正开始使用的时候,我们会遇到需要使用lambda表达式的情形, 所以接下来我们讨论一下Lambda 表达式
///先看下Where的签名 public static IEnumerable<TSource> Where<TSource> (this IEnumerable<TSource> source, Func<TSource,bool> predicate) ///在这里面,有一个Func<TSource, bool> predicate,还记得最早我们用的那句话吗? var nm = names.Where(n => n.Length < 4) ///显然,里面的lambda表达式就是对应这个Func, ///反过来就是这个Func签名对应TSource => bool (以TSource为参数返回bool)的lambda表达式 ///这个Func 是个定义在System.Linq的delegate ///当然,不同的操作符的签名是不一样的,比如Select public static IEnumerable<TResult> Select<TSource,TResult> (this IEnumerable<TSource> source, Func<TSource,TResult> selector) ///这里面,select返回的就不是bool而是TResult,也就是要求是个TSource => TResult的lambda表达式,编译器会推测TResult的类型。 ///此外,还有一种不同的元素类型:TKey用来表示排序,聚合等用的key public static IEnumerable<TSource> OrderBy<TSource,TKey> (this IEnumerable<TSource> source, Func<TSource,TKey> keySelector)
此外,必须注意的一点是,LINQ中,Where和Select操作是保留元素的输入顺序的,比如 a, d, e, g 经过这两种操作符处理后,不管什么规则,选出来的结果还是a一定在d的前面(如果都选出的话)
要了解更多的Linq操作符,可以看此贴
Linq 查询表达式
有了Linq的查询表达式,我们得以用最简化的语法来完成同样的工作,事实上,Query express是仿造LISP等函数式语言的list comprehension的。
对于签名的那个查询,我们可以简化为:
IEnumerable<string> query = from n in names where n.Contains ("a") // Filter elements orderby n.Length // Sort elements select n.ToUpper(); // Translate each element (project)
对于其他查询,我们都可以通过类似的方法来编写,此外,还经常会遇到需要在复杂的查询的情况下使用混合的查询语法。
延迟执行
待续