)的有的代码,的关键职责是背负过滤会集中的数据

[TOC]

  本文重要介绍LINQ查询操作符

 

  LINQ查询为最常用的操作符定义了三个扬言语法。还会有许多询问操作符可用于Enumerable类。

1、LINQ 函数

  上面包车型大巴例子须要用到LINQ基础(一)(http://www.cnblogs.com/afei-24/p/6841361.html)的部分代码

1.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.筛选

1.2、选用数据: 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()的另多个相似函数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”输出了 6次,list1
内的要素是6个,所以能够清楚SelectMany()会服从list1
内的元素个数调用它的selector,并创设集合输出。

 

  LINQ查询利用where子句增加条件表明式来筛选,where子句能够统一四个表明式。

1.3、群组数据: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 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()未有。

var racers = from r in Formula1.GetChampions() 
                    where r.Wins>15 && 
                        (r.Country == "Brazil" || r.Country =="Austria")
                    select r;

        foreach(var r in racers)
        {
            Console.WriteLine("{0:A}", r);
        }

1.4、联接数据: 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()});

 

 

  上述LINQ表明式映射为C#
LINQ查询的增添方法:
  var racers =
Formula1.GetChampions().Where(r =>r.Wins>15 &&
    (r.Country == “Brazil” ||
r.Country ==”Austria”)).Select(r => r);

1.5、数据排序: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
组合,会使得排序被实行四遍,最后的结果会是最终三个OrderBy
所发出的的结果。

  注意,而不是为此查询都可以行使LINQ查询语法,亦不是具有的强大方法都映射到LINQ查询。高端查询要求选拔扩展方法。

1.6、获取集结


     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);

 

2.用索引筛选

1.7、划分并拿走集合


     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
在要素满意条件时,就赶回该因素或是符合一定的索引值条件时回来该因素。

 

  无法动用LINQ查询的三个例证是Where()方法的重载。在WHere()方法的重载中,可以传递第贰个参数————索引。索引是筛选器再次回到的各种结果的计数器。能够在表明式中应用那些目录,实行基于索引的总括:

1.8、访谈成分


   
 IEnumerable<T>本人就是集结对象,所以针对集合对象所急需的要素访问也是少不了的效力,LINQ里的因素访谈效果是判别容器内是还是不是含有成分等。

     首先是获取首尾的成分,分别由First()
以及Last()四个方法担当,它们还各有贰个姐妹方法FirstOrDefault()以及LastOrDefault()前面四个若未有第二个或最终四个成分时,会传出null,而后人会传回其品种的暗许值(基本上正是default(T)的结果)。

     FirstOrDefault() 以及
LastOrDefault()都未曾提供暗中同意的设置情势,因而若想要使用非default(T)的默许值,要利用DefaultEmpty()来设置。First()
Last() 都能传出判别成分是还是不是符合条件的参数,当条件剖断存在时,First
会从集合的近些日子起首扫描,并回到扫描到符合条件的首先个要素,Last
则是扭曲从集合的尾端开头扫描,并返回扫描到符合条件的首先个要素。例:

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,他会在汇聚中仅有一个要素时传回该因素,但若聚积是空的大概有三个以上的要素时会调用例外管理,或是使用它的姐妹方法SingleOrDefault
传回null值,实用性比fisrt和last 低。

     LINQ
提供了ElementAt()本条方式,可根据索引值访问成分,他有个一般方法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(),
另一个是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>则不会引发分歧管理。

 

var racers = Formula1.GetChampions().
            Where((r, index) => r.LastName.StartsWith("A") && index % 2 != 0);
        foreach (var r in racers)
        {
            Console.WriteLine("{0:A}", r);
        }

1.9、聚合与聚集


     聚合运算(aggregation)是聚众数据管理的主要性成效之一,基本的Max
,Min ,Sum ,Average 以及可和煦创立聚合法规的Aggregate()

     Aggregate
是可暂存每一步总计结果的章程,它同意程序员依照传入的标准对各样集结内的因素举办测算,而在历次调用时,他都会将前贰次的结果暂存起来,并作为下一次计量的流传参数。Aggregate
基本上落成三种专业,第一种是向来依据传入的规范化来拍卖一起运算;第二种是可在调用时传出贰个种子值(seed),这些种子值会在开始展开演算时作为标准使用,之后可服从第三次对种子值的运算格局发轫做累计运算;第二种则是在传唱从前做最终的管理,例:

 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、规范的询问操作符

 

3.类型筛选

2.1 筛选


    例:寻觅赢得至少15场较量的碧玺和奥地利共和国(Republik Österreich)赛车手。代码如下:

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);

 

 

  为了拓展基于项指标筛选,能够运用OfType()扩张方法。

2.2 用索引筛选


     不可能接纳LINQ 查询的四个例证是Where 方法的重载。在Where
方法的重载中,能够传递第叁个参数——索引。索引是筛选器再次来到各种结果的计数器。能够在表达式中利用这几个目录,
实行基于索引的企图。下边包车型客车代码由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  

 

object[] data = { "one", 2, 3, "four", "five", 6 };
          var query = data.OfType<string>();
          foreach (var s in query)
          {
            Console.WriteLine(s);
          }

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 */

 

 

  输出:
    one
    four
    five
  从集结仅重返字符串。

2.4 复合的from 子句


   
 假若要求依赖目的的一个分子举行筛选,而该成员本身是三个类别,就能够使用复合的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));

 

4.复合的from子句

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()扩大方法用于提取前十一个结实:

 

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);

 

 

  假诺急需依附指标的分子举行筛选,而该成员本人是二个多元,就能够使用复合from子句。举个例子,LINQ基础(一)(http://www.cnblogs.com/afei-24/p/6841361.html)中的Racer类定义了一个属性Cars,Cars是一个字符串数组。

2.6 分组


     要依赖一个主要字值对查询结果分组,能够使用group子句。
今后一流方程式季军应该根据国家分组,并列出二个国度的季军数。子句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子句创造的佚名类型。在底下的事例中,所返回的国家不光应涵盖国家名和赛车手数量这两性情格,还应涵盖赛车手名体系。这几个行列用贰个授予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 }

 

 

  var ferrariDrivers = from r in Formula1.GetChampions()
                           from c in r.Cars
                           where c == "Ferrari"
                           orderby r.LastName
                           select r.FirstName + " " + r.LastName;

      foreach (var racer in ferrariDrivers)
      {
        Console.WriteLine(racer);
      }

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 }

 

 

     或许统10%多个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 左外连接


     上多个连接示例的出口从一九六〇年始发,因为从今年开首,才同有时候有了超跑手亚军和车队冠军。赛车手亚军出现的更早一些,是在1948年。使用内接连时,唯有找到了协作的记录才回来结果。为了在结果中蕴含全部的年度,能够选择左外联接。左外连接再次回到左边类别中的全部因素,固然它们在侧边的连串中并从未相称的因素。

     上面修改后面的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);

 

 

  第二个from子句访谈Formula1.GetChampions()方法重临的Racer对象,第一个from子句访谈Racer类的Cars属性,以回到所以sting类型的超跑。

2.10 组连接


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

   
 使用组连接时,能够连续不断八个单身的行列,对于在那之中一个队列中的有个别成分,另三个体系中设有对应的一个项列表。

   
 上边包车型客车言传身教使用了多少个单身的种类。四个是前面例子中已经看过的季军人列车表,另三个是二个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
表达式为亚军人列车表中的各类再次来到蕴含三项的一个列表。在那个兰姆da
表明式的达成中,因为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 重返八个Racers
列表,racers 变量重临包括年份、比赛结果和赛车手名字的四个RacerInfo
列表。仅使用姓氏相比八个汇聚中的项是相当不足的。临时候列表中大概同不日常间含有了几个赛车手和他的爹爹,所以必须同偶然候选取FirstName
LastName
进行相比较。那是由此为多个列表创造二个新的无名类型完结的。通过行使into
子句,首个汇聚中的结果被增添到了变量yearResults中。对于第多少个汇集中的每二个跑车手,都创立了二个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 }

 

 

  C#编写翻译器把复合的from子句和LINQ查询转变为SelectMany()扩充方法。SelectMany()扩张方法能够迭代体系中的种类。
  SelectMany()的重载版本:
  public static
IEnumerable<TResult> SelectMany<TSource, TCollection,
TResult>(this IEnumerable<TSource> source,
    Func<TSource,
IEnumerable<TCollection>> collectionSelector,
      Func<TSource, TCollection,
TResult> resultSelector);

2.11 集聚操作


     扩张方法
DistinctUnionIntersectExcept都是集合操作。下边创立二个驾乘法拉利的一流方程式亚军体系和明白迈凯伦的一流方程式亚军连串,然后鲜明是或不是有驾车法拉利和迈凯伦的亚军。

 

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
子句的参数分裂,以获取全数驾车迈凯伦的季军。最棒不要再度编写一样的查询,而得以创建三个办法,个中给它传递参数
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变量必须是贰个信托项目,该委托类型须求三个字符串参数,并回到IEnumerable<Racer>,类似于前方完成的方法。为此,定义了多少个泛型委托Func<>,
所以不必要注解自身的嘱托。把二个拉姆da
表达式赋予racerByCar变量。拉姆da 表明式的右侧定义了七个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艺术总是回到同样的靶子,由此暗中认可的可比操作时有效的,假设不是这种景况,就足以重载集合方法来自定义比较操作。

 

  第叁个参数是隐式参数,它从Formula1.GetChampions()方法接受Racer对象类别。第叁个参数是collectionSelector委托,当中定义了里面体系,是种类的系列,本例子为Cars。第多个参数也是三个信托,为每个Racer对象的Cars属性的各样成分调用这一个委托。

2.12 合并


    Zip() 方法,允许用一个谓词函数把八个有关的队列合併为三个。

   
 首先,创立四个相关的种类,它们利用一样的筛选和排序方法。对于统一,那很主要,因为第贰个汇聚中的第一项会与第二个聚众中的第一项联合,第三个集聚中的第二项会与第二个聚众中的第二项联合,就那样类推。要是七个系列的项数分化,Zip
方法就在达到很小集结的末尾时停下。

     第多少个汇聚中的成分有七个Name属性,首个聚众中的成分有LastName
和Starts 多少个本性

     在racerNames集结上运用Zip
方法,供给把首个聚众(racerNamesAndStarts)作为第一个参数。第2个参数的品种时Func<TFirst, TSecond, TResult>
这几个参数实现为三个Lambda 表明式,它经过参数first
接收第一个汇聚的因素,通过参数second
接收第贰个聚众的成分。其促成代码创制并重临一个字符串,该字符串包涵第贰个汇聚兰秋素的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 }

 

 

  这里Cars是一个字符串数组,会将各类Racer和种种字符串作为参数,调用那么些委托。  

2.13 分区


     增加方法Take 和Skip
等的分区操作可用于分页,举个例子在首先个页面上只展现5个赛车手,在下贰个页面上显得接下去的5个赛车手。

     在上边的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
增加方法,还足以传递一个谓词,依据谓词的结果提取或跳过好几项。

 

var ferrariDrivers = Formula1.GetChampions().SelectMany(
            c => c.Cars, (r, s) => new { Racer=r,Car =s}).Where(
            s =>s.Car == "Ferrari").OrderBy(
            r => r.Racer.LastName).Select(r => r.Racer.FirstName + " " + r.Racer.LastName);

        foreach (var racer in ferrariDrivers)
        {
            Console.WriteLine(racer);
        }

2.14 聚合操作符


     聚合操作符(如 Count、Sum、 Min、马克斯、Average、Aggregate)
不回来贰个种类,而回到一个值。

    Count恢宏方法再次回到集结中的项数。上边包车型地铁Count 方法运用于Racer 的Year
属性,来筛选赛车手,只回去获得亚军次数超越叁次的跑车手,因为同二个查询中供给运用同贰个计数超越一遍,所以使用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
方法用于计算一个国度赢得比赛的总次数。首先依据国家对赛车手分组,再在新创造的佚名类型中,把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办法,
能够传递二个Lambda表明式,该表明式对全体的值进行联谊。

 

5.排序

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>
类只协理四个键应和一个值。在System.Linq名称空间的类Lookup<TKey,TElement>类中,贰个键得以对应七个值。

     使用复合的from 查询,能够摊平赛车手和赛车种类,成立带有Car 和Racer
属性的无名氏类型。在回到的Lookup
对象中,键的连串应是象征小车的string,值的种类应是Racer
为了进行那一个选项,能够给ToLookUp
方法的贰个重载版本传递一个键和二个成分选拔器。键选用器援用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 }

 

 

  要对队列排序,能够选拔前边使用过的orderby.也得以应用orderrby
descending子句(降序)。 

2.16 生成操作符

 

退换操作符Range、
Empty、Repear不是扩大方法,而是重临系列的平常静态方法。在LINQ to Objects
中,那一个艺术可用于Enumerable 类。

      有时须要填写一个范围的数字,此时就应选取Range
方法,这几个法子把第八个参数作为开始值,把第3个参数作为要填写的项数。

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 方法再次来到三个迭代器,该迭代器把同贰个值重复特定的次数。 


var racers = (from r in Formula1.GetChampions()
                      orderby r.Country  descending
                      select r);

        foreach (var racer in racers)
        {
            Console.WriteLine("{0}: {1}, {2}", racer.Country, racer.LastName, racer.FirstName);
        }

  orderby子句深入分析为OrderBy()方法,orderby r.Country
descending分析为OrderByDescending()方法:
  var racers =
Formula1.GetChampions().OrderByDescending(r =>
r.Country).Select(r=>r);

  OrderBy()和OrderByDescending()方法重临IOrderEnumerable<TSource>。那么些接口派生自IEnumerable<TSource>接口,但含有七个极度的法子CreateOrderEnumerable<TSource>()方法。这么些主意用于进一步给连串排序,能够在终极贰个参数钦赐升序照旧降序:
  

// 摘要:
        //     根据某个键对 System.Linq.IOrderedEnumerable<TElement> 的元素执行后续排序。
        //
        // 参数:
        //   keySelector:
        //     用于提取每个元素的键的 System.Func<T,TResult>。
        //
        //   comparer:
        //     用于比较键在返回序列中的位置的 System.Collections.Generic.IComparer<T>。
        //
        //   descending:
        //     如果为 true,则对元素进行降序排序;如果为 false,则对元素进行升序排序。
        //
        // 类型参数:
        //   TKey:
        //     keySelector 生成的键的类型。
        //
        // 返回结果:
        //     一个 System.Linq.IOrderedEnumerable<TElement>,其元素按键排序。
        IOrderedEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, TKey> keySelector, IComparer<TKey> comparer, bool descending);

  例子:

// Create an array of strings to sort.
              string[] fruits = { "apricot", "orange", "banana", "mango", "apple", "grape", "strawberry" };
              // First sort the strings by their length.
              IOrderedEnumerable<string> sortedFruits2 =
                  fruits.OrderBy(fruit => fruit.Length);
              // Secondarily sort the strings alphabetically, using the default comparer.
              IOrderedEnumerable<string> sortedFruits3 =
                  sortedFruits2.CreateOrderedEnumerable<string>(
                      fruit => fruit,
                      Comparer<string>.Default, false);

  使用ThenBy和ThenByDescending()方法进行进一步排序,能够增进猖狂八个:
  var racers =
Formula1.GetChampions().OrderByDescending(r =>
r.Country).ThenByDescending(
    r =>
r.LastName).ThenByDescending(r => r.FirstName).Select(r =>
r);

6.分组

  要基于壹位命关天字值对查询结果分组,能够行使group子句。

// group r by r.Country into g 根据Country属性组合所有的赛车手,并定义为一个新的集合g,用于访问分组的结果信息。
        //select子句创建一个带Country和Count属性的匿名类型。Country = g.Key Key是r.Country
        var countries = from r in Formula1.GetChampions()
                      group r by r.Country into g
                      orderby g.Count() descending, g.Key
                      where g.Count() >= 2
                      select new
                      {
                        Country = g.Key,
                        Count = g.Count()
                      };
        foreach (var item in countries)
          {
            Console.WriteLine("{0, -10} {1}", item.Country, item.Count);
          }

  输出:

  图片 1

  使用扩充方法施行同样的操作,把group r
by r.Country
子句分析为GroupBy()方法。在GroupBy()方法的扬言中,它回到达成了IGrouping<TKey,
TSource>接口的枚举对象。IGrouping<TKey,
TSource>接口定义了Key属性,所以在调用了这么些措施后,能够访谈分组的机要字:
  public static
IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource,
TKey>(this IEnumerable<TSource> source,
    Func<TSource, TKey>
keySelector);
  使用GroupBy方法:

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

7.对嵌套的目的分组

  假使得到的分组的对象须求包涵嵌套的队列,就足以更改select子句成立无名类型。
  

//返回的对象不仅需要包含国家名和赛车手这两个属性,还应包含赛车手集合。
        //使用from r1 in g orderby r1.LastName select r1.FirstName + " " + r1.LastName 内部子句
        var countries = from r in Formula1.GetChampions()
                      group r by r.Country into g
                      orderby g.Count() descending, g.Key
                      where g.Count() >= 2
                      select new
                      {
                        Country = g.Key,
                        Count = g.Count(),
                        Racers = from r1 in g
                                 orderby r1.LastName
                                 select r1.FirstName + " " + r1.LastName
                      };
      foreach (var item in countries)
      {
        Console.WriteLine("{0, -10} {1}", item.Country, item.Count);
        foreach (var name in item.Racers)
        {
          Console.Write("{0}; ", name);
        }
        Console.WriteLine();
      }

8.内连接

  使用join子句能够依据特定的条件合併多个数据源,但在此之前要获得三个一而再的列表。

  使用了LINQ基础(一)(http://www.cnblogs.com/afei-24/p/6841361.html)的代码  

//GetChampions获得冠军赛车手
        var racers = from r in Formula1.GetChampions()
                   from y in r.Years
                   select new
                   {
                     Year = y,
                     Name = r.FirstName + " " + r.LastName
                   };
        //GetContructorChampions获取冠军车队
          var teams = from t in Formula1.GetContructorChampions()
                      from y in t.Years
                      select new
                      {
                        Year = y,
                        Name = t.Name
                      };
        //得到每一年获得冠军的赛车手和车队
        //通过join t in teams on r.Year equals t.Year into rt 子句连接两个数据源
          var racersAndTeams =
            (from r in racers
             join t in teams on r.Year equals t.Year into rt
             from t in rt.DefaultIfEmpty()
             orderby r.Year
             select new
             {
               Year = r.Year,
               Champion = r.Name,
               Constructor = t == null ? "no constructor championship" : t.Name
             });

          Console.WriteLine("Year  Champion\t\t   Constructor Title");
          foreach (var item in racersAndTeams)
          {
            Console.WriteLine("{0}: {1,-20} {2}",
               item.Year, item.Champion, item.Constructor);
          }

 

 

9.左连接
  使用内延续重返相称r.Year equals
t.Year的结果。左连接重返左侧数据源的上上下下要素,即便在左边的多少源中未有相配的因素。
  

var racers = from r in Formula1.GetChampions()
                   from y in r.Years
                   select new
                   {
                     Year = y,
                     Name = r.FirstName + " " + r.LastName
                   };

      var teams = from t in Formula1.GetContructorChampions()
                  from y in t.Years
                  select new
                  {
                    Year = y,
                    Name = t.Name
                  };
        //左连接用join和DefaultIfEmpty方法定义。
        //如果查询到左侧数据源没有和右边数据源Year相同的结果,使用DefaultIfEmpty方法定义右侧的默认值(为空)
      var racersAndTeams =
        (from r in racers
         join t in teams on r.Year equals t.Year into rt
         from t in rt.DefaultIfEmpty()
         orderby r.Year
         select new
         {
           Year = r.Year,
           Champion = r.Name,
           Constructor = t == null ? "no constructor championship" : t.Name
         });

      Console.WriteLine("Year  Champion\t\t   Constructor Title");
      foreach (var item in racersAndTeams)
      {
        Console.WriteLine("{0}: {1,-20} {2}",
           item.Year, item.Champion, item.Constructor);
      }

10.组连接

  组连接类似内连接,内三番五次通过某一项连接八个数据源(如 r.Year equals
t.Year),组连接使用一组项连接,举个例子下边包车型客车例证,
  通过 new
    {
      FirstName =
r.FirstName,
      LastName =
r.LastName
    }
    equals
    new
    {
      FirstName =
r2.FirstName,
      LastName =
r2.LastName
    }
  连接几个数据源
  

var racers = Formula1.GetChampionships()
        .SelectMany(cs => new List<RacerInfo>()
        {
         new RacerInfo {
           Year = cs.Year,
           Position = 1,
           FirstName = cs.First.FirstName(),
           LastName = cs.First.LastName()        
         },
         new RacerInfo {
           Year = cs.Year,
           Position = 2,
           FirstName = cs.Second.FirstName(),
           LastName = cs.Second.LastName()        
         },
         new RacerInfo {
           Year = cs.Year,
           Position = 3,
           FirstName = cs.Third.FirstName(),
           LastName = cs.Third.LastName()        
         }
       });

      var q = (from r in Formula1.GetChampions()
               join r2 in racers on
               new
               {
                 FirstName = r.FirstName,
                 LastName = r.LastName
               }
               equals
               new
               {
                 FirstName = r2.FirstName,
                 LastName = r2.LastName
               }
               into yearResults
               select new
               {
                 FirstName = r.FirstName,
                 LastName = r.LastName,
                 Wins = r.Wins,
                 Starts = r.Starts,
                 Results = yearResults
               });

      foreach (var r in q)
      {
        Console.WriteLine("{0} {1}", r.FirstName, r.LastName);
        foreach (var results in r.Results)
        {
          Console.WriteLine("{0} {1}", results.Year, results.Position);
        }
      }

 

11.成团操作

  扩张方法Distinct(),Union(),Intersect()(获取交集),Except()都以汇聚操作。  

//获取同时驾驶Ferrari和驾驶McLaren获得过冠军的赛车手
        static void SetOperations()
        {
            //定义一个委托,用来查询驾驶Ferrari获得过冠军的赛车手和驾驶McLaren获得过冠军的赛车手
          Func<string, IEnumerable<Racer>> racersByCar =
              car => from r in Formula1.GetChampions()
                     from c in r.Cars
                     where c == car
                     orderby r.LastName
                     select r;

          Console.WriteLine("World champion with Ferrari and McLaren");
          //使用Intersect方法获取两个数据源的交集
          foreach (var racer in racersByCar("Ferrari").Intersect(racersByCar("McLaren")))
          {
            Console.WriteLine(racer);
          }
        }

 

12.合并
  Zip()方法是.NET
4.0新扩充的,允许用叁个为此函数把三个相关的队列合併为多个。

  对于联合,第二个汇聚中的第一项与第一个聚众的首先项联合,第4个集聚中的第二项与第2个集聚的第二项联合,就那样推算。如若五个种类的项数差异,Zip()方法就能够在完结十分的小集结的末段时停下。

  第三个聚众中的成分有八个Name属性,第一个聚众中的成分有LastName和Starts属性。

  在racerNames集合上选用Zip()方法,要求把第二个集结racerNamesAndStarts作为第贰个参数。第贰个参数是三个委托,它通过参数first接受第三个聚众的元素,通过参数second接受首个汇聚的要素。其落实代码重返叁个字符串:  

var racerNames = from r in Formula1.GetChampions()
                       where r.Country == "Italy"
                       orderby r.Wins descending
                       select new
                       {
                         Name = r.FirstName + " " + r.LastName
                       };

          var racerNamesAndStarts = from r in Formula1.GetChampions()
                                    where r.Country == "Italy"
                                    orderby r.Wins descending
                                    select new
                                    {
                                      LastName = r.LastName,
                                      Starts = r.Starts
                                    };


          var racers = racerNames.Zip(racerNamesAndStarts, (first, second) => first.Name + ", starts: " + second.Starts);
          foreach (var r in racers)
          {
              Console.WriteLine(r);
          }

13.分区

  扩充方法Take()和Skip()等的分区操作可用来分页。

  举个例子,在首先页只突显5个赛车手,下一页展现接下去的5个赛车手…
  Skip(page *
pageSize)方法调到钦赐索引出,忽略后面包车型地铁多寡。Take(pageSize)方法展现pageSize条数据
  

 int pageSize = 5;

          int numberPages = (int)Math.Ceiling(Formula1.GetChampions().Count() /
                (double)pageSize);

          for (int page = 0; page < numberPages; page++)
          {
            Console.WriteLine("Page {0}", page);

            var racers =
               (from r in Formula1.GetChampions()
                orderby r.LastName, r.FirstName
                select r.FirstName + " " + r.LastName).
               Skip(page * pageSize).Take(pageSize);

            foreach (var name in racers)
            {
              Console.WriteLine(name);
            }
            Console.WriteLine();
          }

  TakeWhile()和SkipWhile()方法,传递三个寄托,满足这一个标准的数量就领取或跳转:
  public static
IEnumerable<TSource> SkipWhile<TSource>(this
IEnumerable<TSource> source, Func<TSource, bool> predicate);

14.成团操作符

  聚合操作符(如Count(),Sum(),Min(),马克斯(),Average(),Aggregate())不回来多少个队列,而是回到三个值。

  举例,使用Count方法应用于Racer的Years属性,筛选得到亚军次数当先3次的赛车手。因为一再行使r.Years.Count(),所以选拔let子句定义了多少个变量。  

  

var query = from r in Formula1.GetChampions()
                  let numberYears = r.Years.Count()
                  where numberYears >= 3
                  orderby numberYears descending, r.LastName
                  select new
                  {
                    Name = r.FirstName + " " + r.LastName,
                    TimesChampion = numberYears
                  };

          foreach (var r in query)
          {
            Console.WriteLine("{0} {1}", r.Name, r.TimesChampion);
          }

  Aggregate()方法传递四个委托,将数据源中的每一种成分作为委托的参数,并运用内定的函数累加。

15.转变操作符

  LINQ基础(一)(http://www.cnblogs.com/afei-24/p/6841361.html)提到,查询会推迟到迭代数据项时才实践,使用转变操作符会马上实践查询,把询问结果放在数组,列表和字典中。  

    

//转换为数组
        var names = new List<string> { "Nino", "Alberto", "Juan", "Mike", "Phil" };

        var namesWithJ = (from n in names
                         where n.StartsWith("J")
                         orderby n
                         select n).ToList();

  转换为Lookup<TKey,TElement>
  

//把Car赛车属性作为键,每个键关联多个车手Racer
            var racers = (from r in Formula1.GetChampions()
                      from c in r.Cars
                      select new
                      {
                          Car = c,
                          Racer = r
                          }).ToLookup(cr => cr.Car, cr => cr.Racer);
            foreach (var v in racers)
            {
                Console.Write(v.Key+"........");
                foreach (var k in racers[v.Key])
                {
                    Console.WriteLine(k);
                }
            }

  ToLookup(cr => cr.Car, cr =>
cr.Racer)方法的三个重载版本传递八个键和贰个要素选拔器

  借使供给在非类型化的集纳上利用LINQ查询,能够利用Cast()方法,定义强类型化的查询:  

var list = new System.Collections.ArrayList(Formula1.GetChampions() as System.Collections.ICollection);

          var query = from r in list.Cast<Racer>()
                      where r.Country == "USA"
                      orderby r.Wins descending
                      select r;
          foreach (var racer in query)
          {
            Console.WriteLine("{0:A}", racer);
          }

 

  Cast<Racer>()将
System.Collections.IEnumerable 的要素强制调换为钦赐的品类。

16.生成操作符

  生成操作符Range(),Empty(),Repeat()方法不是扩张方法,而是回到体系的健康静态方法。在LING
to Object中,那些艺术可用于Enumerable类。

  Range()方法用来填充二个限量的数字。第二个参数作为开首值,第二个参数作为要填写的项数:
    var values =
Enumerable.Range(1,20);
  结果为1至20的集合。

  能够把该结果与别的扩充方法统一:
    var values =
Enumerable.Range(1,20).Select(n=> n*3);

  Empty()方法重返一个不重回值的迭代器,它用来需求多少个聚众的参数,当中能够给参数字传送递空集结。

  Repeat()方法再次回到钦点个数的重复值的集合迭代器。

相关文章