11、分区
扩展方法Take()和Skip()等用于分区操作,这些操作可用于数据分页。在下面的Linq查询中,把扩展方法添加到查询最后,Skip()方法或略掉根据分页大小和页数得到的项数,Take()方法根据分页大小提取一定数目的项:
- int pagesize = 5;
- int numberPages = (int)Math.Ceiling(Formula1.GetChampions().Count / (double)pagesize);//获得页数
- for (int i = 0; i < numberPages; i++)
- {
- Console.WriteLine("Page {0}", i);
- var racers = (from r in Formula1.GetChampions()
- orderby r.LastName
- select r.FirstName + " " + r.LastName)
- .Skip(i * pagesize) //跳过指定数目的元素
- .Take(pagesize) //提取指定数目的元素?
- .TakeWhile(r => r.Length > 11); //额外的限制条件
- foreach (var item in racers)
- {
- Console.WriteLine(item);
- }
- Console.WriteLine();
- }
Math.Ceiling (Decimal) | 返回大于或等于指定的十进制数的最小整数。 |
Math.Ceiling (Double) | 返回大于或等于该指定双精度浮点数的最小整数。 |
输出结果是几组赛手。
12、聚合操作
聚合操作用于对集合项进行简单的运算,返回结果是一个值而不是一组数据。
下面演示使用Count()方法筛选出冠军次数操作3的赛手:
- //统计获得冠军次数大于3次的运动员和获胜次数
- var query = from r in Formula1.GetChampions()
- where r.Years.Count() > 3
- orderby r.Years.Count() descending
- select new
- {
- Name = r.FirstName + " " + r.LastName,
- TimesChampion = r.Years.Count()
- };
- foreach (var item in query)
- {
- Console.WriteLine(item.Name + " " + item.TimesChampion.ToString());
- }
- Console.WriteLine();
又如Sum()方法,返回序列中的所有数字的和。下面的示例演示了使用Sum()方法计算一个国际赢得的比赛次数,首先根据国家对赛手分组,在创建的匿名类中,对Wins属性进行计算:
- //计算一个国际获得冠军的总次数
- var countries =
- from c in
- from r in Formula1.GetChampions()
- group r by r.Country into c1
- select new
- {
- Country = c1.Key,
- Wins = (from r1 in c1 select r1.Wins).Sum()
- }
- orderby c.Wins descending
- select c;
13、结果转换
查询结果可以通过调用扩展方法转换成其他类型。但是需要注意的是,如果进行了类型转换,Linq查询将会立即执行而不是推迟到访问数据项时才执行。
下面是一个简单的例子:
- //简单查询
- var query = (from r in Formula1.GetChampions()
- where r.Starts > 150
- orderby r.Starts descending
- select r).ToList();
- foreach (var item in query)
- {
- Console.WriteLine("{0} {0:S}",item);
- }
- Console.WriteLine();
- //扩展方法查询
- var query2 = Formula1.GetChampions()
- .Where(r => r.Starts > 150)
- .OrderByDescending(r => r.Starts)
- .ToList()
- .Select(r => r) ;
- foreach (var item in query2)
- {
- Console.WriteLine("{0} {0:S}", item);
- }
- Console.WriteLine();
- //生成查找表
- var query3 = (from r in Formula1.GetChampions()
- from c in r.Cars
- select new { Car = c, Racer = r })
- .ToLookup(cr => cr.Car, cr => cr.Racer);
- if (query3.Contains("Williams"))
- {
- foreach (var item in query3["Williams"])
- {
- Console.WriteLine(item);
- }
- }
这里建查询结果使用ToList()方法转换成了List<T>类型。
14、生成操作符
生成操作符Ranger()、Empty()、Repear()不是扩展方法,而是返回序列的正常静态方法。
下面例子返回一个填充了一个范围数字的变量,使用了Ranger()方法对变量进行填充
- //var values = Enumerable.Range(1, 12);
- var values = Enumerable.Range(1, 12)
- .Where(r=>r%2 == 0)
- .Select(r => r * 2);
- foreach (var item in values)
- {
- Console.Write("{0} ",item);
- }
- Console.WriteLine();
提示:Range()方法不返回填充了所定义值的集合,这个方法与其他方法一样,也推迟执行查询,返回一 个RangeEnumerator,其中只有一个yield return 语句,来递增值。
可以把该结果与其他扩展方法合并起来,获得另一个结果,例如使用Select()扩展方法:
Empty()方法返回一个不返回值的迭代器,它可以用于参数需要一个集合,且可以给参数传送空集合的情形。
Repeat()方法返回一个迭代器,该迭代器把同一个值重复特定的次数。
15、并行Linq
并行Linq是.Net4新增加的。在.Net 4的System.Linq命名空间中新增加了类ParallelEnumerable,可以分解查询工作使其工作在多个线程上。
这里首先准备一个大型集合,用随机数填充之后使用Linq筛选数据,获取筛选数据的总和。该查询使用where子句定义一个筛选器,汇总值小于20的项,最后使用Sum()方法计算:
- const int arraySize = 100000000;
- var data = new int[arraySize];
- var r = new Random();
- for (int i = 0; i < arraySize; i++)
- {
- data[i] = r.Next(40);
- }
- var sum = (from x in data.AsParallel()
- where arraySize < 20
- select x).Sum();
- sum = data.AsParallel().Where(x => x > 20).Select(x => x).Sum();
- Console.WriteLine(sum);
可以看出,这里的查询与前面的查询仅仅是使用了AsParallel()方法。
运行这段代码时启动任务管理器就可以看见效果了。此时系统上所有的CPU都处于忙碌状态,如果删除了AsParallel方法,就不会有这个效果了。