C#基础:LINQ 查询函数整理

[TOC]

 

1、LINQ 函数

1.① 、查询结果过滤 :where()


Enumerable.Where() 是LINQ
中应用最多的函数,超过四分一都要对准集合对象开始展览过滤,由此Where()在LINQ
的操作上到处可见,Where()的要紧义务是承受过滤集合中的数据:其原型如下:

1 public static IEnumerbale<TSouce> Where<TSource>(this IEnumerable<Tsource> source,Func<TSource,bool> predicate);
2 public static IEnumerable<TSource>where<TSource> (this IEnumerable<TSource> source,Func<TSource,int,bool> predicate);

 

   Where()的参数是用来过滤成分的尺码,它供给标准必须传回bool,以分明此因素是不是符合条件,或是由特定的要素初始算起(使用Func<TSource,int bool>,中间的传遍参数代表该因素在聚集中的索引值),例如要在一个数列集合中找出超过5的数字时:

1 List<int> list1=new List<int>(){6,4,2,7,9,0};
2  
3 list1.Where(c=>c>5);

 

或者

1 list1.Where(c=>c>=1).Where(c=>c<=5);
2  
3 list1.Where(c=>c>=1&&c<=5); 

 

 Where()的评定圭臬是,只要判断函数再次回到true 就确立,反之则废除。

1.② 、选取数据: Select()、SelectMany()


     经常在编写LINQ
函数调用时较少用到选用数据的函数(因为函数调用会直接回到IEnumerable<T>
集合对象 ),但在编写LINQ语句时那些常用,在言辞中若编写了select new一声令下,它会被编写翻译器转换来LINQ
Select(),Select() 的原型如下:

1 public static IEnumerable<TResult> Select<TSource,TResult>(this IEnumerable<TSource> source,Func<TSource,TResult> selector);
2 public static IEnumerable<TResult> Select<TSource,TResult>(this IEnumerable<TSource> source,Func<TSource,int,TResult> selector);

 

      与Where()类似,Select() 也可以按照元素所在的位置判断处理,而Select()所指定的处理式selector 必须传回一个对象,这个对象可以是现有的类型,也可以是匿名的类型,既可以通过Select() 来重新组装所需数据。例:

1 var query=db.OrderDetails.Where(o=>o.ID==12345).Select(o=>new{ ProductID=o.ProductID,Qty=o.Qty});

 

     
Select()的另2个相似函数SelectMay()则是处理有五个汇聚对象来源的数额选择,其原型如下:

1     public static IEnumerable<TResult> SelectMany<TSource,TResult>(this IEnumerable<TSource> source,Func<TSource,IEnumberable<TResult>> selector);
2     public static IEnumerable<TResult> SelectMany<TSource,TResult>(this IEnumerable<TSource> source,Func<TSource,int,IEnumberable<TResult>> selector);
3     public static IEnumerable<TResult> SelectMany<TSource,TCollection,TResult>(this IEnumerable<TSource> source,Func<TSource,IEnumberable<TCollection>> collectionSelector,Func<TSource,TCollection,TResult> resultSelector);
4     public static IEnumerable<TResult> SelectMany<TSource,TCollection,TResult>(this IEnumerable<TSource> source,Func<TSource,int,IEnumberable<TCollection>> collectionSelector,Func<TSource,TCollection,TResult> resultSelector);

 

      SelectMany() 在LINQ 函数调用上较难理解,但如果把它想象成数据库

CROSS JOIN ,相对来说就便于懂了,例: 

1 List<int> list1=new List<int>(){1,2,3,4,5,6};
2 List<int> list2=new List<int>(){6,4,2,7,9,0};
3  
4 var query=list1.SelectMany(o=>list2);
5 foreach(var q in query)
6      Console.WriteLine("{0}",q);

 

      输出结果:

1 6424790642790642790642790642790642790

 

      因为“642790”出口了 九次,list1
内的因素是5个,所以能够理解SelectMany()会安分守纪list1
内的要素个数调用它的selector,并组建集合输出。

 

1.叁 、群组数据:GroupBy()、ToLookup()


   
 汇总数据是查询机制的基本功效,而在集中此前,必需要先将数据做群组化,才能展开计算,LINQ
的群组数据成效由Enumerable.GroupBy()函数提供。

     GroupBy()
会根据给定的key(keySelector)以及内容(elementSelector),爆发群组后的结果(IGroup接口对象可能由resultSelector变动的结果对象),例:

 1 List<int> sequence =new List<int>(){1,2,3,4,3,2,4,6,4,2,4};
 2  
 3 var group=sequence.GroupBy(o=>o);
 4 foreach(var g in group)
 5 {
 6      Console.WrilteLine("{0} count:{1}",g.Key,g.Count());
 7 /*计算每个数出现的次数。
 8      GroupBy 设置了使用数列本身值作为Key值,并且利用这个Key 分组产生分组的数据(IGrouping<TKey,TElement类型),再对分组的数据进行汇总。结果如下:
 9  
10 1 count: 1
11 2 count: 3
12 3 count: 2
13 4 count: 4
14 6 count: 1
15 
16 */

 

     如果想要在回来在此之前对分组后的因素做拍卖,能够流传elementSelector
而倘若要在要素处理后发生结果的话,则能够流传resultSelector,那样回去的集合会是以resultSelector
重返的类别为主,而不是暗中同意的IGroup接口。

   
 除了GroupBy()能群组化数据外、此外二个全体群组化数据能力的是ToLookUp(),它能够扭转具有群组化本性的聚合对象,由ILookup<TKey,TElement>组成。

   
ToLookup()看起来和GroupBy()多少类似,不过它会其余生成三个新的集结对象,那一个集合对象由ILookup<TKey,TElement>所构成,允许三个键值存在,且1个键值可含蓄众多涉及的实值。例:

 1 var nameValuesGroup=new[]
 2 {
 3      new{name="Allen", value=65,group="A"},
 4      new{name="Abbey",value=120,group="B"},
 5      new{name="Sue",Value=200,group="A"}
 6 };
 7 var lookupValues=namValuesGroup.ToLookup(c=>c.group);
 8 foreach(var g in lookupValues)
 9 {
10      Console.WriteLine("===Group: {0}===",g.Key);
11      foreach(var item in g)
12      {
13           Console.WriteLine("name:{0},value:{1}",item.name,item.value);
14      }
15 }

 

      GroupBy()本身有所延缓执行的风味,而ToLookup()没有。

1.四 、联接数据: Join() 与GroupJoin()


   
 身为一个询问机制,将多个汇集实行衔接(join)也是当然的,尤其是在开始展览数量的相比较和汇总时,联接机制显得更关键。在LINQ
函数中,有 Enumerable.Join() 函数负责处理联接,其原型如下:

public static IEnumerable<TResult> Join<TOuter,TInner,TKey,TResult>(this IEnumerable<TOuter> outer,IEnumerable<TInner> inner,Func<TOutput,TKey> outerKeySelector,Func<TInner,TKey> innerKEySelector,Func<TOuter,TInner,TResult> resultSelector)

 

 

     由原型可观察它将原来的集纳视为TOuter,而将盛传的聚合视为TInner,儿还要控制由哪位属性或成员当Key,最后由resultSelector来输出联接的结果。例:

1 var query=from item1 in list1
2      join item2 in list2 on item1 equals item2
3      select item2;
4 var query3=list1.Join(
5      list2,
6      item1=>item1,
7      item2=>item2,
8      (item1,item2)=>item2
9      );

 

     Enumerable<T>.Join()利用的是INNER JOIN的概念,当TInner.Key
TOuter.Key如出一辙时,才会将成分输出到resultSelector 作为参数。

     方今常用的连接方式,INNER JOINEnumerable<T>.Join()
实现,CROSS JOINEnumerable<T>.SelectMany() 完毕,还有一种JOIN
方式尚未设想,LEFT OUTER JOIN形式,要促成那一个形式,供给求依靠GroupJoin()措施来贯彻。

    GroupJoinJoin() 十三分相似,可是它却又Join()
GroupBy()两边的效应,在Join() 的状态下,它会留下TInner
TOuter两边都有的值,但在GroupJoin(),它会将TOuter
的值作为Key,并依此来对TInner 做群组化后输出,例:

1 var query4=from item1 in list1
2           join item2 in list2 on item1 equals item2 into g
3           from item in g.DefaultIfEmpty()
4           select new{ v=item1,c=item};
5 var query5=list1.GroupJoin(
6           list2,
7           item1=>item1,
8           item2=>item2,
9           (item1,item2)=>new {v=item1,c=item2.Count()});

 

 

1.伍 、数据排序:OrderBy() 与ThenBy()


     数据排序是在数据处理甘肃中国广播公司泛的效劳,在LINQ
内的排序主如果以OrderBy函数为主,而为了援救连续条件的排序,可增加ThenBy
函数,以便处理多重规范排序的要求。基于LINQ的延迟查询机制,排序也不是在一起来就开始展览的,而是在数据真的被访问时才会议及展览开排序。因而OrderBy()在拍卖集合时,传递回来的是名为IOrderedEnumerable<T>
接口的对象。

   
 OrderByThenBy还有七个貌似的法子,差距只在乎做反向排序。OrderByDescending
ThenByDescending

     观望函数的原型,会意识OrderBy盛传的是IEnumerable<T>
,但ThenBy传播的是IOrderedEnumerable,所以一般在排序时先调用OrderBy,再使用ThenBy开始展览多重排序。假诺贰个集合有A和B多少个属性,假若想要先为A排序再为B排序,则要选取OrderBy(A).ThenBy(B)的主意来进展排序,OrderByThenBy三回调用只可以设置叁个字段,在拓展多重原则时,必须先调用OrderBy,再按要求调用ThenBy壹遍或频仍。例:

 1  var nameValues=new[]
 2 {
 3      new {name="Allen",value=64},
 4      new {name="abbey",value=120},
 5      new {name="slomng",value=330},
 6      new {name="george",value=213}
 7 };
 8 //single sort
 9 var sortedNames=nameValues.OrderBy(c=>c.name);
10 var sortedValues=nameValues.OrderBy(c=>c.value);
11  
12 //multiply sort conditions
13 var sortedByNameValues=nameValues.OrderBy(c=>c.name).ThenBy(c=>c.value);
14 var sortedByValueNames=nameValues.OrderBy(c=>c.value).ThenBy(c=>c.name);

 

     
假诺要安装多重排序条件,请务必使用OrderBy()加上ThenBy()的组合,若使用OrderBy +OrderBy
组合,会使得排序被实践一回,最终的结果会是最终2个OrderBy
所发出的的结果。

1.六 、获取集合


     LINQ 所处理的数目都由集合而来,因此将LINQ
执行的结果转换到集合也很简单。LINQ本人补助种种差异的集合生成格局,蕴含生成数组的ToArray()、生成列表的ToList、生成字典集合的ToDictionary
以及变更Lookup<TKey,TElement>类的ToLookup。例:

1 var arrayOutput=nameValues.ToArray();
2 var listOutput=nameValues.ToList();
3  
4 var dictOutput1=nameValues.ToDictionary(c=>c.name);
5 var dictOutput2=nameValues.ToDictionary(c=>c.name,c=>value);

 

1.七 、划分并获取集合


     Skip()SkipWhile()Take()
TakeWhile()。在数据库查询时,为了达到最佳的习性,在数据量大时要举行分页处理(paging)。上面三个函数的效益正是在大集合内切出少量数据。

 1 public static IEnumberable<TSource> Skip<TSource>(
 2      this IEnumerable<TSource> source,
 3      int count
 4 )
 5 public static IEnumberable<TSource> SkipWhile<TSource>(
 6      this IEnumerable<TSource> source,
 7      Func<TSource,bool> predicate
 8 )
 9 public static IEnumberable<TSource> SkipWhile<TSource>(
10      this IEnumerable<TSource> source,
11      Func<TSource,int ,bool> predicate
12 )
13 public static IEnumberable<TSource> Take<TSource>(
14      this IEnumerable<TSource> source,
15      int count
16 )
17 public static IEnumberable<TSource> TakeWhile<TSource>(
18      this IEnumerable<TSource> source,
19      Func<TSource,bool> predicate
20 )
21 public static IEnumberable<TSource> TakeWhile<TSource>(
22      this IEnumerable<TSource> source,
23      Func<TSource,int ,bool> predicate
24 )

 

      Skip()用来在集结中跳跃,让LINQ
核心直接将游标跳到内定的任务,而不用经过“巡航”来移动,在大型集合中可节约千千万万岁月,而SkipWhile
也有一致成效,但多了判断式,也正是跳过符合条件的因素,而各异的SkipWhile()可用来决定要跳过符合条件的或者判断跳过一定的索引值。

     Take()用来传播集合中一定数量的要素,它会告知LINQ
宗旨直接重返它所钦命的元素数量,很合乎选拔与分页的效用。TakeWhile
则是和SkipWhile 类似都是多了尺度判断式,不过TakeWhile
在要素满意条件时,就回去该因素或是符合一定的索引值条件时再次来到该因素。

 

1.⑧ 、访问成分


   
 IEnumerable<T>笔者就是会晤对象,所以本着集合对象所急需的成分访问也是少不了的法力,LINQ里的因素访问效果是判定容器内是不是带有成分等。

     首先是获得首尾的要素,分别由First()
以及Last()七个点子负责,它们还各有三个姐妹方法FirstOrDefault()以及LastOrDefault()前者若没有第③个或最终1个成分时,会传出null,而后人会传回其品种的暗许值(基本上正是default(T)的结果)。

     FirstOrDefault() 以及
LastOrDefault()都不曾提供默许的装置情势,因而若想要使用非default(T)的暗中同意值,要运用DefaultEmpty()来设置。First()
Last() 都能流传判断成分是或不是符合条件的参数,当条件判断存在时,First
会从集合的前方初始扫描,并再次回到扫描到符合条件的首先个因素,Last
则是扭曲从集合的尾端开端扫描,并赶回扫描到符合条件的第2个成分。例:

1 var firstLastItems=new []{"zero","two","three","four","five"};
2 string firstContainsO=firstLastItems.First(s=>s.Contains('o'));
3 string lastContainsO=firstLastItems.Last(s=>s.Contains('0'));

 

  LINQ
内还有二个Single,他会在联谊中唯有1个成分时传回该因素,但若集合是空的或然有多个以上的要素时会调用例外处理,或是使用它的姐妹方法SingleOrDefault
传回null值,实用性比fisrt和last 低。

     LINQ
提供了ElementAt()C#,以此艺术,可服从索引值访问成分,他有个一般方法ElementAtOrDefault作用和firstordefault/lastordefault
是相同的。当找不到成分时就重回暗中认可值。例:

1 var firstLastItems=new []{"zero","two","three","four","five"};
2 string itematThree=firstLastITems.ElementAt(2);

 

      若要判断集合内有没有一定值,LINQ 提供了Contains,
能够判断集合捏有没有扩散的要素,但因为Contain
会判断目的是还是不是等于,所以它其它提供了三个可传唱IEqualityComparer<T>
的当作相比依照的重载(overload)方法,可用以自定义类对象的约等于比较操作。

     若要认清集合内有没有值,LINQ 提供了多少个点子,一个是Count(),
另1个是Any(),除了可以大致判断集合内有没有值外,也得以流传衡量圭表来决定是还是不是要列入总计。经常会习惯使用Count
来判断集合内是还是不是存在任何因素,为何要多做贰个Any啊。其实是考虑到LINQ
大概的查询对象会含有远程数据库,不自然唯有本地的数据源。对于远程的数据源,如果利用Count
,要费用较高的财力来读取数据后进行计数在传出,但一旦使用Any(),则远程只要判断符合条件的数量是不是存在一笔即可,不要求总体计数,所以本着中长途数据源,使用Any
来判断有无数据是较好的挑三拣四。针对地方的集合 any 和count 差不离一向不异样。

     若要看清集合内的要素是不是全部契合特定条件时, 能够动用LINQ 的All(),
它能够依据传入的规格来围观全体因素,唯有在拥有因素都符合条件时,或是集合时间和空间时才会回到true
,否则会重返false。

     若要遵从成分的体系进行筛选的话,除了选拔Where
对各样成分做类型音讯判断外,LINQ 也提供了二个更简便的不二法门
OfType<T>(),它能够传回集合内符合T所内定项指标音讯,那么些方法很符合用在联谊内含有了已达成了过多接口的类对象。然后利用OfType<T>服从接口类型实行筛选。

     OfType<T>还有一个像样方法Cast<T>
,功能与OfType <T>相同,但Cast<T>会打算把集合内的要素类型转换到T类型,若不也许实行类型转换时会调用InvalidCastException
例外处理。若采取OfType<T>则不会吸引分化处理。

 

1.玖 、聚合与集中


     聚合运算(aggregation)是集结数据处理的主要意义之一,基本的Max
,Min ,Sum ,Average 以及可协调制订聚合规则的Aggregate()

     Aggregate
是可暂存每一步总计结果的点子,它同意程序员依据传入的尺码对种种集合内的因素进行总括,而在每一次调用时,他都会将前一遍的结果暂存起来,并视作下次划算的不胫而走参数。Aggregate
基本上实现两种工作,第①种是直接遵照传入的规范来处理一起运算;第1种是可在调用时传出1个种子值(seed),这几个种子值会在起始举行演算时作为规范使用,之后可坚守第1次对种子值的运算格局开首做累计运算;第三种则是在扩散以前做最终的处理,例:

 1 double myBalance=100.0;
 2  
 3 int[] withdrawItems={20,10,40,50,10,70,30};
 4  
 5 double balance=withdrawItems.Aggregate(myBalance,(originbalance,nextWithdrawal)=>{
 6      Console.WriteLine("originbalance:{0},nextWithdrawak:{1}",originbalance,nextdrawal);
 7      Console.WriteLine("Withdrawal status:{0}",(nextWithdrawal<=originbalance)?"OK":"FAILED");
 8  
 9      return ((nextWithdrawal<=originbalance)?(originbalance-nextWithdrawal):originbalance);
10 });
11 Console.WriteLine("Ending balance:{0}:",balance);

 

 若要对最终的储蓄值实行处理,即可使用第四个参数resultSelector,例:

 

1 var balanceStatus=
2 withdrawItems.Aggregate(myBalance,(originbalance,nextWithdrawal)=>{
3      return((nextWithdrawal<=originbalance)?(originbalance-nextWithdrawal):originbalance);
4  
5 },
6 (finalbalance)=>
7 {
8      return (finalbalance>=1000)?"Normal":"Lower";
9 });

 

 

二 、标准的询问操作符

 

2.1 筛选


    例:找出赢得至少15场比赛的碧玺和奥地利共和国(The Republic of Austria)赛车手。代码如下:

1 var racers=from r in Formula1.GetChampions()
2           where r.Wins > 15 && (r.Country=="Brazil"||r.Country=="Austria")
3           select r;
4  
5 foreach(var r in racers)
6 {
7      Console.WriteLine("{0:A}",r);
8 }

 

      下边采取Where()  和 Select() 的代码:

1 var racers=Formula1.GetChampions().
2           Where(r=>r.Wins>15 && (r.Country=="Brazil" || r.Country=="Austria")).
3           Select(r=>r);

 

 

2.2 用索引筛选


     不能利用LINQ 查询的二个例证是Where 方法的重载。在Where
方法的重载中,能够传递第三个参数——索引。索引是筛选器再次来到每一个结果的计数器。可以在表明式中使用那几个目录,
执行基于索引的一个钱打二14个结。下边包车型地铁代码由Where
扩大方法调用,它使用索引重临姓氏以“A” 开始,索引为偶数的超跑手。

1 var racers=Formula1.GetChamptions().
2           Where((r,index)=>r.LastName.StartsWith("A") && index % 2 !=0);
3  
4 foreach(var r in racers)
5 {
6      Console.WriteLine("{0,A}",r);
7 }
8  

 

2.3 类型筛选


   
 为了举办基于项指标筛选,能够选取OfType恢宏方法。那里数组数据包涵string
和 int 对象。
使用OfType扩张方法,把string类传递给泛型参数,就从集合中回到字符串。

1 object[] data={"ones",1,3,"fre","fdfs",333};
 2 var query=data.OfType<string>();
 3 foreach(var s in query)
 4 {
 5      Console.WriteLine(s);
 6 }
 7  /*
 8      运行结果为:
 9  
10 ones
11 fre
12 fdfs
13 */

 

 

2.4 复合的from 子句


   
 若是急需依照目的的一个成员进行筛选,而该成员本人是1个层层,就能够利用复合的from
子句。Racer 类定义了叁本性质Cars,在这之中Cars
是叁个字符串数组。要筛选开车阿斯顿·马丁的兼具季军,可以运用如下所示的LINQ
查询。第一个from子句访问从Formula1.GetChampion()办法再次回到的Race
对象,第一个from 子句访问Racer的 Cars 属性。以回到全数string
类型的赛车。接着在where 子句中应用那些赛车筛选开车迈巴赫的具备季军。

1 var ferrariDrivers=from r in Formula.GetChampions()
2                     from c in r.Cars
3                     where c=="Ferrari"
4                     orderby r.LastName
5                     select r.FirstName +" "+ r.LastName;

 

     C# 编写翻译器把适合的from 子句和LINQ 查询转换为SelectMany
扩展方法。当中实例所用的重载版本如下

1 public static IEnumerable<TResult> SelectMany<TSource,TCollection,TResult>(this IEnumerable<TSource> source,Func<TSource,IEnumberable<TCollection>> collectionSelector,Func<TSource,TCollection,TResult> resultSelector);

 

     第一个参数是隐式参数,它从 Get.Champions()方法中接收Racer 对象序列。第二个参数是collectionSelector委托,其中定义了内部序列。在Lambda 表达式 r=>r.Cars 中,应返回赛车集合。第三个委托参数是一个委托,现在为每个赛车调用给委托,接收Racer 和Car 对象。Lambda 表达式创建了以匿名类型,他有Racer 和 Car 类型。 这个SelectMany方法的结果是摊平了赛车手和赛车的层次结构,为每辆赛车返回匿名类型的一个新对象集合。

 

1 var ferrariDrivers= Formula1.GetChampion().
2                     SelectMany(r=>r.Cars,
3                          (r,c)=>new{Racer=r,Car=c}.
4                          where(r=>r.Car=="Ferrari").
5                          OrderBy(r=>r.Racer.LastName).
6                          Select(r=>r.Racer.FirstName+" "+r.Racer.LastName));

 

2.5 排序


  要对队列排序,前边使用了 orderby
子句。上面复习一下前边使用的orderby descending
子句的例子。当中赛车手依据赢得比赛的次数实行降序排序,赢得比赛的次数用关键字采取器内定。

1 var racers=from r in Formula1.GetChampions()
2            where r.Country=="Brazil"
3            orderby r.Wins descending
4            select r;

 

     orderby
子句解析为OrderBy( ) 方法,orderby descending子句解析为OrderByDescending方法

 

1 var racers= Formula1.GetChampions().
2           Where(r=>r.Country=="Brazil").
3           OrderByDescending(r=>r.Wins).
4           Select(r=>r);

 

     使用LINQ
查询时,只需把持有用于排序的不比首要字(用逗号隔开分离)添加到orderby子句中。在下例中,全体的超跑手先依照国家排序,再遵照姓氏排序,最后依据名字排序。添加到LINQ
查询结果中的Take()壮大方法用于提取前13个结实:

 

1 var racers=(from r in Formula1.GetChampions()
2                orderby r.Country,r.LastName,r.FirstName
3                select r).Take(10);

 

     使用OrderByThenBy扩大方法能够实施同一的操作

1 var racers=Formula1.GetChamptions().
2      OrderBy(r=>r.Country).
3      ThenBy(r=>r.LastName).
4      ThenBy(r=>r.FirstName).
5      Take(10);

 

 

2.6 分组


     要基于1个主要字值对查询结果分组,可以采纳group子句。
今后顶级方程式亚军应该依照国家分组,并列出1个国度的季军数。子句group r by r.County into g根据
Country 属性组合具有的超跑手,并定义三个新的标识符g
它以后用于访问分组的结果音讯。group子句的结果应当根据使用到分组结果上的壮大方法Count来排序,假设季军数一致,就根据重庆大学字排序,该重大字是国家,因为那是分组使用的首要性字。where
子句依照至少有两项的分组来筛选结果。select
子句创造叁个带CountryCount属性的匿名类型。

 

 1 var countries= from r in Formula1.GetChampions()
 2                group r by r.Country into g
 3                orderby g.Count() descending, g.Key
 4                where g.Count() >=2
 5                select new {
 6                               Country=g.Key,
 7                               Count=g.Count()
 8                          };
 9 foreach(var item in countries)
10 {
11      Console.WriteLine("{1,-10} {1}",item.Country,item.Count);
12 }

 

     

     接下来把子句
group r by r.Country into g解析为GroupBy(r=>r.Country),重返分组系列。分组种类首先用OrderByDescending艺术排序,再用ThneBy
方法排序。接着调用WhereSelect 方法

1 var countries= Formula1.GetChampions().
2                GroupBy(r=>r.Country).
3                OrderByDescending(g=>g.Count()).
4                ThenBy(g=>g.Key).
5                Where(g=>g.Count()>=2).
6                Select(g=>new {Country=g.Key,Count=g.Count()});

 

 

2.7 对嵌套的指标分组


   
 假诺分组的目的应包蕴嵌套的队列,就足以变更select子句创造的匿名类型。在下边包车型大巴事例中,所重临的国度不仅应包罗国家名和赛车手数量那七个属性,还应涵盖赛车手名类别。这一个行列用1个给予Racers属性的from/ in
内部子句钦点,内部的from
子句使用分组标识符g取得该分组中的全数赛车手,用姓氏对它们排序,再依照姓名制造三个新字符串。

     

 1 var countries=from r in Formula1.GetChampions()
 2                group r by r.Country into g
 3                orderby g.Count() descending, g.Key
 4                where g.Count()>=2
 5                select new
 6                {
 7                     Country=g.Key,
 8                     Count=g.Count(),
 9                     Racers=from r1 in g
10                            orderby r1.LastName
11                            select r1.FirstName +" "+ r1.LastName
12                };
13 foreach(var item in countries)
14 {
15      Console.WriteLine("{0,-10} {1}",item.Country,item.Count);
16      foreach(var name in item.Racers)
17      {
18           Console.WriteLine("{0};",name);
19      }
20      Console.WirteLine(); 
21 }

 

 

2.8 内连接


     使用join
子句能够依照特定的尺度合并三个数据源,但前面要收获几个要连接的列表。在超级方程式比赛后,有赛车手季军和车队季军。赛车手从GetChampions
方法中回到,车队从GetConstructionChampions措施中回到。今后要博取两个年度列表,列出每年的赛车手季军和车队亚军。

 

 1 var racers= from r in Formula1.GetChampions()
 2             from y in r.Years
 3             select new
 4             {
 5                Year=y,
 6                Name=r.FirstName+" "+r.LastName
 7              };
 8  
 9 vat teams=from t in Formula1.GetConstructorChampions()
10           from y in t.Years
11           select new
12           {
13                Year=y,
14                Name=t.Name
15           };
16 var racersAndTeams=(from r in racers
17                     join t in teams on r.Year equals t.Year
18                     select new
19                     {
20                          r.Year,
21                          Champion=r.Name,
22                          Constructor=t.Name
23                     }).Take(10);
24 Console.WriteLine("Year World Champion\t Constructor Title");
25 foreach(var item in racersAndTeams)
26 {
27      Console.WriteLine("{0}:{1,-20} {2}",item.Year,item.Champion,item.Constructor);
28 }

 

 

     也许统百分之十一个LINQ 查询

 

 1 var racersAndTeams=(from r in
 2                     from r1 in Formula1.GetChampions()
 3                     from yr in r1.Years
 4                     select new
 5                     {
 6                          Year=yr,
 7                          Name=r1.FirstName+" "+r1.LastName
 8                     }
 9                     join t in
10                          from t1 in Formula1.GetConstructorChampions()
11                          from yt in t1.Years
12                          select new
13                          {
14                               Year=yt,
15                               Name=t1.Name
16                          }
17                     on r.Year equals t.Year
18                     orderby t.Year
19                     select new
20                     {
21                          Year=r.Year,
22                          Racer=r.Name,
23                          Team=t.Name
24                     }).Take(10);

 

 

2.9 左外连接


     上1个连接示例的输出从1960年起来,因为从这一年发轫,才同时有了超跑手亚军和车队季军。赛车手季军现身的更早一些,是在一九四九年。使用内连接时,唯有找到了合作的记录才回去结果。为了在结果中蕴藏全体的年度,能够运用左外联接。左外连接重临左侧体系中的全体成分,即便它们在左边的体系中并没有匹配的因素。

     上面修改前边的LINQ 查询,使用左外连接。左外连接使用 join
子句和DefaultIfEmpty
方法定义。假设查询的左侧(赛车手)没有匹配的车队亚军,那么就采用DefaultIfEmpty办法定义其出手的默许值。

 

 1 var racersAndTeams=
 2      (from r in racers
 3      join t in teams on r.Year equals t.Year into rt
 4      from t in rt.DefaultIfEmpty()
 5      orderby r.Year
 6      select new
 7      {
 8           Year=r.Year,
 9           Champion=r.Name,
10           Constructor=t==null?"no constructor championship":t.Name
11      }).Take(10);

 

 

2.10 组连接


     左外连接使用了组连接和into
子句。它有一部分语法与组连接相同,只可是组连接不使用DefaultIfEmpty方法。

   
 使用组连接时,可以连绵起伏八个独立的队列,对于里边一个队列中的有个别成分,另多少个系列中设有对应的3个项列表。

   
 下边包车型地铁言传身教使用了五个独立的队列。四个是近期例子中一度看过的季军人列车表,另2个是3个ChampionShip项指标聚合。下边包车型客车代码段彰显了Championship类。

 

1 public class Championship
2 {
3      public int Year{get;set;}
4      public string First{get;set;}
5      public string Second{get;set;}
6      public string Third{get;set;}
7 }

 

 

GetChampionships 重临了亚军集合

 

 1 private static List<Championship> championships;
 2 public static IEnumerable<Championship> GetChampionships()
 3 {
 4      if(championships == null)
 5      {
 6           championships=new List<Championship>();
 7           championships.Add(new Championship
 8           {
 9                Year=1950,
10                First="Nino Farina",
11                Second="Juan Manuel Fangio",
12                Third="Luigi Fagioli"
13           });
14           championships.Add(new Championship
15           {
16                Year=1951,
17                First="Juan Manuel Fangio",
18                Second="Alberto Ascari",
19                Third="Froliab Gonzalez"
20           });
21      }

 

 

   
 季军人列车表应与每一个亚军年份中获得前三名的赛车手构成的列表组合起来,然后展现每一年的结果。

    RacerInfo类定义了要出示的音信,如下所示:

1 public class RacerInfo
2 {
3      public int Year{get;set;}
4      public int Position {get;set;}
5      public string FirstName{get;set;}
6      public string LastName{get;set;}
7 }

 

     

     使用连接语句能够把多少个列表中的赛车手组合起来。

   
 因为冠军人列车表中的每一项都包括八个赛车手,所以率先要求把这些这几个列表摊平。一种方法是选择SelectMany
方法,该办法运用的Lambda
表明式为季军人列车表中的每一项再次回到包括三项的多个列表。在那个Lambda
表明式的落到实处中,因为RacerInfo 蕴含FirstNameLastName
属性,而接受的聚合只含有带有First 、Second、Third
属性的一个称号,所以必须拆分字符串,那能够由此扩充方法 FirstName
SecondName 完成。

 

 1 var racers=Formula1.GetChampionships()
 2           .SelectMany(cs=>new List<RacerInfo>()
 3           {
 4                new RacerInfo{
 5                     Year=cs.Year,
 6                     Position=1,
 7                     FirstName=cs.First.FirstName(),
 8                     LastName=cs.Last.LastName()
 9                },
10                new RacerInfo{
11                     Year=cs.Year,
12                     Position=2,
13                     FirstName=cs.Fisrt.FirstName(),
14                     LastName=cs.Last.LastName()
15                },
16                new RacerInfo{
17                     Year=cs.Year,
18                     Position=3,
19                     FirstName=cs.First.FirstName(),
20                     LastName=cs.Last.LastName()
21                }
22           });

 

 

     扩张方法FirstName 和SecondName 使用空格字符拆分字符串:

 

 1 public static class StringExtension
 2 {
 3      public static string FirstName(this string name)
 4      {
 5           int ix=name.LastIndexOf(' ');
 6           return name.Substring(0,ix);
 7      }
 8      public static string LastName(this string name)
 9      {
10           int ix=name.LastIndexOf(' ');
11           return name.Substring(ix+1);
12      }
13 }

 

 

     今后就能够接连五个种类。Formula1.GetChampions 再次来到3个Racers
列表,racers 变量重回包含年份、赛果和赛车手名字的一个RacerInfo
列表。仅使用姓氏比较四个集聚中的项是不够的。有时候列表中恐怕还要涵盖了3个赛车手和她的老爸,所以必须同时选取FirstName
LastName
进行相比。那是经过为多少个列表制造1个新的匿名类型完毕的。通过动用into
子句,第一个聚众中的结果被添加到了变量yearResults中。对于第3个聚众中的每二个跑车手,都创建了3个yearResults.
它涵盖了在其次个聚众中匹配名和姓的结果。最终,用LINQ
查询创造了二个饱含所需音信的新匿名类型。

 

 1 var q=(from r in Formula1.GetChampions()
 2           join r2 in racers on
 3           new
 4           {
 5                FirstName=r.FirstName,
 6                LastName=r.LastName
 7           }
 8           equals
 9           new
10           {
11                FisrtName=r2.FirstName,
12                LastName=r2.LastName
13           }
14           into yearResults
15           select new
16           {
17                FirstName=r.FirstName,
18                LastName=r.LastName,
19                Wins=r.Wins,
20                Stars=r.Stars,
21                Results=yearResults
22           });
23 foreach(var r in q)
24 {
25      Console.WriteLine("{0} {1}",r.FirstName,r.LastName);
26      foreach(var results in r.Results)
27      {
28           Console.WriteLine("{0} {1}.",results.Year,results.Position);
29      }
30 }

 

 

2.11 汇聚操作


     扩充方法
DistinctUnionIntersectExcept都是集结操作。下边创设一个精通雷克萨斯的一级方程式季军类别和精晓Cadillac的一流方程式亚军种类,然后分明是还是不是有驾乘Bentley和阿斯顿·马丁的季军。

 

1 var ferrariDrivers=from r in
2                     Formula1.GetChampions()
3                     from c in r.Cars
4                     where c =="Ferrari"
5                     orderby r.LastName
6                     select r;

 

 

     今后建立另一个基本相同的询问,但where
子句的参数不相同,以得到全体驾乘Porsche的季军。最好不用再一次编写相同的询问,而可以创制1个情势,在那之中给它传递参数
car

 

1 private static IEnumerable<Racer> GetRacersByCar(string car)
2 {
3      return from r in Formula1.GetChampions()
4                from c in r.Cars
5                where c==car 
6                orderby r.LastName
7                select r;
8 }

 

 

   
 然则,因为该措施不要求再其余地点使用,所以\应定义叁个寄托项指标变量来保存LINQ
查询,*racerByCar变量必须是三个信托项目,该委托类型供给3个字符串参数,并回到IEnumerable<Racer>,类似于前方完成的主意。为此,定义了多少个泛型委托Func<>,
所以不必要评释自身的嘱托。把四个拉姆da
表明式赋予racerByCar变量。Lambda 表达式的左边定义了三个car
变量。其项目时Func 委托的首先个泛型参数(字符串)。左边定义了LINQ
查询,它利用该参数和where 子句:

 

1 Func<string , IEnumerable<Racer>> racersByCar=
2                car=>from r in Formula1.GetChampions()
3                     from c in r.Cars
4                     where c==car
5                     orderby r.LastName
6                     select r;

 

 

     现在得以接纳Intersect 增添方法
,获得开车阿尔法·罗米欧和克莱斯勒的保有亚军:

     

1 Console.WriteLine("World champion with Ferrari and McLaren");
2 foreach(var racer in racersByCar("Ferraris").Interesect(racersByCar("McLaren")))
3 {
4      Console.WirteLine(racer);
5 }

 

     

  集合操作通过调用实体类的GetHashCodeEquals
方法来比较对象。对于自定义相比,仍是能够传递多少个兑现了IEqualityComparer<T>接口的对象。在这些示例中,GetChampions措施总是回到相同的目的,因而暗中同意的相比操作时有效的,假诺不是那种地方,就足以重载集合方法来自定义比较操作。

 

2.12 合并


    Zip() 方法,允许用3个谓词函数把多个相关的队列合并为三个。

   
 首先,创制七个相关的体系,它们接纳同一的筛选和排序方法。对于统一,那很重庆大学,因为第1个聚众中的第壹项会与第三个聚众中的第①项联合,第2个汇集中的第2项会与第二个聚众中的第叁项联合,以此类推。如若四个体系的项数不相同,Zip
方法就在抵达较小集合的末段时停下。

     第二个聚众中的成分有贰个Name属性,第一个汇聚中的成分有LastName
和Starts 五个属性

     在racerNames集合上选用Zip
方法,要求把第三个集聚(racerNamesAndStarts)作为第①个参数。第二个参数的门类时Func<TFirst, TSecond, TResult>
那一个参数达成为三个拉姆da 表明式,它经过参数first
接收第三个聚众的因素,通过参数second
接收第③个集聚的要素。其促成代码创造并回到2个字符串,该字符串包蕴第二个汇聚兰秋素的Name属性和第③个聚众申月素的Starts
属性。

 

 1 var racerNames=from r in Formula1.GetChampions()
 2                where r.Country =="Italy"
 3                orderby r.Wins descending
 4                select new
 5                {
 6                     Name=r.FirstName +" "+ r.LastName
 7                };
 8 var racerNamesAndStarts=from r in Formula1.GetChampions()
 9                          where r.Country="Italy"
10                          orderby r.Wins descending
11                          select new
12                          {
13                               LastName=r.LastName,
14                               Starts=r.Starts
15                          };
16 var racers=racerNames.Zip(racerNamesAndStarts,(first,second)=>first.Name+", starts: "+second.Starts);
17 foreach(var r in racers)
18 {
19      Console.WriteLine(r);
20 }

 

 

2.13 分区


     扩张方法Take 和Skip
等的分区操作可用来分页,例如在第一个页面上只显示三个赛车手,在下三个页面上显得接下去的6个赛车手。

     在下边包车型地铁LINQ 查询中,把增加方法Skip 和Take 添加到查询的结尾。Skip
方法先忽略根据页面大小和实在页数总结出的项数,再采用Take()
方法依据页面大小提取一定数额的项。

 

1 int pageSize=5;
 2  
 3 int numberPages=(int)Math.Ceiling(Formula1.GetChampions().Count()/(double)pageSize);
 4 for(int page=0;page<numberPages;page++)
 5 {
 6      Console.WriteLine("Page {0}",page);
 7      var racers=(from r in Formula1.GetChampions()
 8                     orderby r.LastName,r.FirstName
 9                     select r.FirstName+" "+r.LastName).
10                     Skip(page*pageSize).Take(pageSize);
11      foreach(var name in racers)
12      {
13           Console.WriteLine(name);
14       }
15      Console.WriteLine();
16 }
17  

 

   
 那个分页机制的多个宗旨是,因为查询会在每一种页面上实行,所以改变底层的数码会潜移默化结果。在继续执行分页操作时,会显得新对象。依照不一致的事态,那对于应用程序恐怕有利于。即使那么些操作时不必要的,就能够只对本来的数据源分页,然后利用映射导到原有数据上的缓存。

     使用TakeWhileSkipWhile
扩充方法,还足以传递2个谓词,依照谓词的结果提取或跳过一些项。

 

2.14 聚合操作符


     聚合操作符(如 Count、Sum、 Min、马克斯、Average、Aggregate)
不回去一个行列,而回到二个值。

    Count恢宏方法再次来到集合中的项数。上边包车型大巴Count 方法运用于Racer 的Year
属性,来筛选赛车手,只回去获得季军次数超越一遍的跑车手,因为同3个查询中要求选择同四个计数抢先贰遍,所以利用let
子句定义了多个变量 numberYear

 

 1 var query=from r in Formula1.GetChampions()
 2           let numberYears=r.Years.Count()
 3           where numberYear>=3
 4           orderby numberYears descending, r.LastName
 5           select new
 6           {
 7                Name=r.FirstName+" "+r.LastName,
 8                TimesChampion=numberYears
 9           };
10 foreach(var r in query)
11 {
12      Console.WriteLine("{0} {1}",r.Name,r.TimesChampion);
13 }     
14  

 

     Sum 方法汇总体系中的全数数字,再次来到这几个数字的和。上面包车型客车Sum
方法用于总括1个国度获得竞技的总次数。首先依据国家对赛车手分组,再在新创设的匿名类型中,把Wins
属性赋予有个别国家获得比赛的总次数。

 

 1 var countries=(from c in from r in Formula1.GetChampions()
 2                group r by r.Country into c
 3                select new
 4                {
 5                     Country=c.Key,
 6                     Wins=(from r1 in c select r1.Wins).Sum()
 7                }
 8                orderby c.Wins descending, c.Country
 9                select c).Take(5);
10 foreach(var country in countries)
11 {
12      Console.WriteLine("{0} {1}",country.Country,country.Wins);
13 }

 

     对于Aggergate措施,
可以传递3个拉姆da表明式,该表明式对具备的值实行联谊。

 

2.15 转换操作符


   
 前边提到,查询能够推迟到走访数据项时在推行。在迭代中央银行使查询时,查询会执行。而选拔转换操作符会立刻执行查询,把询问结果放在数组、列表或字典中。

     在底下的事例中,调用ToList
扩展方法,立即施行查询,获得的结果放在List<T> 类中。

     

1 List<Racer> racers=(from r in Formula1.GetChampions()
2                     where r.Starts>150
3                     orderby r.Starts descending
4                     select r).ToList();
5 foreach(var racer in racers)
6 {
7      Console.WriteLine("{0} {0:S}",racer);
8 }

 

     

   
 把重临的指标放在列表中并不曾那么粗略。例如,对于集合类中从赛车到赛车手的急忙访问。能够动用新类Lookup<TKey,TElement>

     

  Dictionary<TKey,TValue>
类只协助3个键应和一个值。在System.Linq名称空间的类Lookup<TKey,TElement>类中,二个键得以对应多个值。

     使用复合的from 查询,能够摊平赛车手和赛车连串,创制带有Car 和Racer
属性的匿名类型。在回到的Lookup
对象中,键的品类应是意味小车的string,值的项目应是Racer
为了举办这几个选项,可以给ToLookUp
方法的贰个重载版本传递1个键和一个因素选择器。键采用器引用Car
属性镁成分选拔器引用Racer 属性。

 

 1 var racers=(from r in Formula1.GetChampions()
 2                from c in r.Cars
 3                select new
 4                {
 5                     Car=c,
 6                     Racer=r
 7                }).ToLookup(cr=>cr.Car,cr=>cr.Racer);
 8 if(racers.Contains("Williams"))
 9 {
10      foreach(var williamsRacer in Racers["Williams"])
11      {
12           Console.WriteLine(williamsRacer);
13      }
14 }

 

 

     假诺急需在非类型化的集合上(如ArrayList)使用LINQ
查询,就足以行使Cast
方法。在下边包车型地铁例证中,基于Object类型的ArrayList会见用Racer
对象填充。为了定义强类型化的查询,能够利用Cast 方法

 

 1 var list=new System.Collections.ArrayList(Formula1.GetChampions() as System.Collections.ICollection);
 2  
 3 var query= from r in list.Cast<Racer>()
 4           where r.Country=="USA"
 5           orderby r.Wins descending
 6           select r;
 7 foreach(var racer in query)
 8 {
 9      Console.WriteLine("{0:A}",racer);
10 }

 

 

2.16 生成操作符

 

转移操作符Range、
Empty、Repear不是扩大方法,而是再次来到种类的不荒谬静态方法。在LINQ to Objects
中,那么些措施可用于Enumerable 类。

      有时要求填写二个限制的数字,此时就应选取Range
方法,那些形式把首个参数作为起先值,把第②个参数作为要填写的项数。

1  
2 var values =Enumerable.Range(1,20);
3 foreach(var item in values)
4 {
5      Console.WriteLine("{0}",item);
6 }
7 Console.WriteLine();
8  
9 //结果 1 2 3 4 5 6 ......  19 20

 

 

Range 方法
不回来填充了所定义值的聚集,那一个措施与其他措施同样,也推迟执行查询,并再次来到二个RangeEnumerator
,个中唯有一条yield return 语句,来递增值。

 

   
 能够把该结果与此外扩充方法统一起来,得到另贰个结果。例如,使用Select
扩大方法

 

1 var values =Enumerable.Range(1,20).Select(n=>n*3);

 

 

     Empty
方法重返一个不重返值的迭代器,他得以用来供给多个集聚的参数,在那之中能够给参数字传送递空集合。

     Repeat 方法重回一个迭代器,该迭代器把同贰个值重复特定的次数。 


相关文章