在上一篇随笔中,对lambda表达式进行了简单的介绍,对lambda表达式的
lambda表达式应用到实际程序中。lambda表达式在LINQ中
Linq to objects是
一个数组进行排序,就可以使用SQL风格的LINQ to Objects进行这个数组进行排序。
在
括.NET2.0之前的非泛型接口。 序列(Sequences),我们对实现了IEnumerable<T>接口的集合称为序列。标准查询符,
下面
| from, in | 定义LINQ查询表达式结构,从指定数据集合中提取出数据或新的数据集合 |
| where | 对从数据集合中的数据进行约束限制 |
| select | 从数据集合中选取出数据 |
| join, on, equals, into | 通过特定的键对数据集进行联结 |
| orderby,ascending,descending | 对提取出来的子集按升序或降序进行排序 |
| group, by | 对提取出来的子集按特定的值进行分组 |
大多数的操作符都
式可以方便对序列(Sequences)进行复杂的查询操作,而不需要每次在调用System.Linq.IEnumerable的静态函数的时候将数据集合作为第一个参数传入,
代码示例如下。
1 void Main()
2 {
3 string[] items={"csharp","cpp","python","perl","java"};
4 var lens1= System.Linq.Enumerable.Select(items,n=>n.Length);//静态函数
5 var lens2= items.Select(items,n=>n.Length);//扩展函数
6 lens1.Dump("长度列表1");
7 lens2.Dump("长度列表2");
8 }
在.NET2.0之前有很多遗留的集合类,比如ArrayList,Stack,Hashtable等非泛型的集合类, 由于没有实现IEnumerable<T>接口,因此我们不能直接
1 void Main()
2 {
3 ArrayList list=new ArrayList(){"csharp","java","vb"};
4 //编译错误: Could not find an implementation of the query pattern for source type
5 //'System.Collections.ArrayList'. 'Select' not found. Consider explicitly
6 //specifying the type of the range variable 'item'.
7 var error=from item in list
8 select item;
9 //OK
10 var ok=from item in list.Cast<string>()
11 select item;
12 //OK too
13 IEnumerable<string> ok2=list.OfType<string>().Select(n=>n);
14 }
2. LINQ的延迟查询
在上一个示例代码中,IEnumerable<string> ok2=list.OfType<string>().Select(n=>n); ok2保存
存了查询出来的序列结果,其实不然,select函数并没有把查询出来的结果返回,而只有在IEnumerable<T>被遍历列举的时候才会真正返回查询结果集合。
下面我们通过代码示例来验证:
1 void Main()
2 {
3 string[] items={"csharp","vb","java","cpp","python"};
4 IEnumerable<string> result=items.Where(n=>n.Length>4);
5 //显示查询结果
6 Console.WriteLine("--------enumerated-----------");
7 foreach(string item in result)
8 {
9 Console.WriteLine(item);
10 }
11 items[0]="not exist"; //修改数组的内容
12 //再次显示查询结果
13 Console.WriteLine("--------enumerated again-----------");
14 foreach(string item in result)
15 {
16 Console.WriteLine(item);
17 }
18 }
19
结果:
--------enumerated-----------
csharp
python
--------enumerated again-----------
not exist
python
我们注意示例代码,result两次遍历发生了变化,因为我们直接修改了数组的内容。我们可以得出结论:查询被延迟了,当我们对IEnumerable<T>进行
遍历列举的时候,序列中的元素才会被yield返回。
现在我们来讨论下延迟查询的作用。延迟查询有什么有点和什么缺点呢?
优点:1. 执行过程中减少资源的占用,提高性能。数据只在
2. 实际保存的
1 void Main()
2 {
3 int[] list={1,2,3,4,5,6,7};
4 var result=list.Where(n=>n>4);
5 result.Dump("结果}
在例子中,我们查询条件
因此延迟查询可以保证我们一直能够得到想要的数据集合。
缺点:数据可能不一致造成异常。由于我们的查询结果
面我们总结的延时查询的优点2,在某些时候也会成为我们的错误。因此在
下面我们还
1 void Main()
2 {
3 int[] list={1,2,3,4,5,6,7};
4 var result=list.Select(n=>100/n);
5 result.Dump(); //正常
6 //..其他代码
7 list[6]=0;
8 //..其他代码
9 result.Dump();//除以0异常
10 }
那么我们如何让查询不
方法可以将查询结果返回。
1 void Main()
2 {
3 int[] list={1,2,3,4,5,6,7};
4 var result=list.Select(n=>100/n).ToList();
5 result.Dump();//result保存查询结果
6 }
在查询操作符之中,有部分操作符并不
(图片来源:《Pro LINQ: Language Integrated Query in C# 2008》):
3.扩展函数
前面我们说到过,IEnumerable<T>和IEnumerable
可以直接跳过.扩展函数允许你直接在已经存在的类型上添加函数,而不用修改原有类型,或者通过继承的方式.扩展函数
型的实例来调用,下面
1 public static IEnumerable<T> Where<T>(
2 this IEnumerable<T> source,
3 Func<T, int, bool> predicate);
上面是IEnumerable<T>的标准操作符where,下面我们举例实现自己的扩展操作符,扩展string类型增加函数统计指定的字符的数量GetCharCount(),
这里我们需要注意几点:
1.推荐定义自己的域名空间,统一管理扩展函数。
2.扩展函数和所在的类都要
3.允许重载现有的函数。
1 namespace MyExtension //自定义域名空间
2 {
3 public static class StringExtension //静态类
4 { //统计字符在字符串串中出现的次数
5 public static int GetCharCount(this string source, char c) //静态函数
6 {
7 return (from item in source where item == c select item).Count();
8 }
9 }
10 }
1 using MyExtension //引用我们扩展域名空间
2 namespace ConsoleApplication1
3 {
4 static class Program
5 {
6 static void Main(string[] args)
7 {
8 string demoString = "this is test string";
9 int count= demoString.GetCharCount('i'); //直接调用扩展函数
10 Console.WriteLine(x);
11 }
12 }
13 }
4.常见查询操作符
在经过了上面对linq to objects的了解,在这一节中,我们对常见的操作符进行说明,在这里会根据操作符的
附注:后面的示例中用到的序列都为 items,定义如下:
string[] items={"charp","cpp","python","perl"};
1. Where
作用: 过滤序列,将结果放入新序列中
参数重载1: Func<T,bool> 委托
参数重载2: Func<T,int,bool>委托 (int 标识索引index)
var result=items.Where(p=>p.Length==4);
//描述: 返回字符串长度为4的序列var result=item.Where((p,i)=>i==1); //i为索引
//描述: 返回索引为1的字符串
2. Select
作用:对序列元素进行操作,返回新的结果序列(返回序列的类型和原序列类型可以不同)
参数重载1:Func<T, int, S> selector
参数重载2:Func<T, S> selector
var result=items.Select(p=>p.Length);
//描述: 返回序列中所有元素的长度var result=items.Select((p,i)=>p+":"+i)//描述: 返回序列中所有元素和序号的组合序列
3. SelectMany
作用:创建新的一对多关系的序列,将原序列中每个元素进行操作转换为新的序列。
参数重载1:Func<T, IEnumerable<S>> selector
参数重载2:Func<T, int, IEnumerable<S>> selector
var result=items.SelectMany(p=>p.ToArray());
//描述:将序列中string元素转换为字符数组,即结果是 “cpp”被转换为IEnerable<Char> 值为{ ‘c’,‘p’,‘p’} var result=items.SelectMany((p,i)=>i==1?p.ToArray():new char[]{})//描述: 将序列中序号为1的元素转换为字符数组,其他的元素转换为空字符数组
4. Take
作用:从原序列中获取指定数量的元素集合,返回新的序列。
参数:int count
var result=items.Take(1);
//描述: 返回序列的第一个元素 结果为"Csharp"
5. TakeWhile
作用:从原序列中yield满足条件的元素,直到遇到不满足条件的元素,剩余的其他元素将被忽略。
参数重载1:Func<T, bool> predicate
参数重载2:Func<T, int, bool> predicate
string[] s={"1","22","333","4444","555","66","7"};var result= s.TakeWhile(p=>p.Length<4);//描述: 返回满足长度小于4的序列,注意:当遍历到"4444"的时候不满足条件yield结束,后面的元素虽然满足条件但是 被忽略 //结果:122333
6. Skip
作用:从原序列中跳过指定数量的元素,返回剩余元素组成的序列。
参数:int count
var result= items.Skip(2);//描述: 跳过两个元素返回剩余的元素组成的序列//结果: {"python","perl"};
7. SkipWhile
作用:从原序列中跳过满足条件的元素,返回剩余元素组成的序列。
参数:Func<T, int, bool> predicate
var result= items.SkipWhile(p=>p.Contains(‘h’));//描述: 跳过元素中有包含字符h的元素,返回其他元素//结果: {"cpp","perl"};
7. OrderBy 和 OrderByDescending
作用:从原序列中跳过满足条件的元素,返回剩余元素组成的序列。
参数:Func<T, int, bool> predicate
var result= items.SkipWhile(p=>p.Contains(‘h’));//描述: 跳过元素中有包含字符h的元素,返回其他元素//结果: {"cpp","perl"};
8.
作用:在已排序后的序列进行第二种方式排序。
是否延迟:Yes
重载参数1:Func<T, K> Selector
重载参数2:Func<T, K> Selector,IComparer<K> comp
1 var result=items.OrderBy(s=>s.Length).ThenBy(s=>s[0]);
//描述:先按照长度排序,然后按照字符串首字母排序
9.Join
作用:通过指定的键连接多个序列组合成新序列,和SQL的内连接相似。
是否延迟:Yes
参数:IEnumerable<U> inner,Func<T, K> outerKey,Func<U, K> innerKey,Func<T, U, V> result
1 void Main()
2 {
3 string[] items1={"aa","bbb","ccc","dddd"};
4 string[] items2={"eee","ff","gggg","h","iii"};
5 var r=items1.Join(items2,i=>i.Length,j=>j.Length,(i,j)=>new {result=string.Format("{0}:{1}",i,j)});
6 r.Dump();
7 }
8 //将两个序列按照长度连接组成新序列
结果:
result
aa:ff
bbb:eee
bbb:iii
ccc:eee
ccc:iii
dddd:gggg
注意:
1.返回的序列是匿名类型,可以自定义结果序列的列,但是同时需要指定名称(result就是名称)。
2.由于是匿名类型,因此只能使用var关键字
10.GroupBy
作用:对序列按照指定方式进行分组,生产新的分组序列
是否延迟:Yes
参数:Func<T, K> keySelector
返回值:IEnumerable<IGrouping<K, T>>
1 void Main()
2 {
3 string[] items={"1","22","33","444","555","6666"};
4 var r1=from item in items
5 group item by item.Length into g
6 select new {g.Key};
7 r1.Dump();
8
9 //等同于下面
10 var r2= items.GroupBy(item=>item.Length).Select(g=>g.Key);
11 r2.Dump();
12 }
结果:
1
2
3
4
11.Union
作用:组合两个序列为一个序列,去除重复的元素
是否延迟:Yes
参数:IEnumerable<T> second
1 void Main()
2 {
3 string[] items={"1","22","6666"};
4 string[] items2={"1","22","33",,"777"};
5 items.Union(items2).Dump();
6 }
结果:
1
22
33
6666
777
12.ToArray , ToList
作用:将序列转换为数组 或 列表
是否延迟:No
参数:none
1 void Main()
2 {
3 string[] items={"1","22","33","444","555","6666"};
4 var r= items.Where(n=>n.Length>2).ToArray();
5 var r2= items.Where(n=>n.Length>2).ToList();
6 }
7 //r和r2保存的是实际的数组和列表,而不是查询条件对象
13.ToDictionary
作用:转换序列为泛型字典Dictionary of type <K, T>
是否延迟:No
参数:Func<T, K> keySelector,Func<T, E> elementSelector,IEqualityComparer<K> comparer
1 void Main()
2 {
3 string[] items={"1","22","33","444","555","6666"};
4 items.ToDictionary(k=>k.Length).Dump();//错误:重复键
5 items.ToDictionary(k=>k,k=>k.Length).Dump();
6 }
结果:
key value
1 1
22 2
33 2
444 3
555 3
6666 4
5.后记
在上面的例子中只列出了几个常见,某些比较简单的操作符比如:Min,Max,Count,ElementAt等从字面意思都可以看出来就没有做描述。
由于理解不够深入,存在的问题请指正。
---------------------------------------------------------------------
作者: jordan51341 (jordan51341@163.com)
欢迎转载,转载请注明原文地址:http://www.cnblogs.com/jordan51341
---------------------------------------------------------------------