Linq To Object标准操作符最强总结

Where OfType<TResult>:

说明:筛选操作符定义了返回元素的条件。Where查询操作符中,可以使用谓词。例如Lambda表达式定义的谓词,来返回布尔值。OfType<TResult>根据类型筛选元素,只返回TResult类型的元素。


Select和SelectMany:

投射操作符用于把对象转为另一个类型的新的对象。Select和SelectMany定义了根据选择器函数选择结果值得投射。


OrderBy、ThenBy、OrderByDescending、ThenByDescending、Reverse:

排序操作符改变返回的元素的顺序。OrderBy按升序排序,OrderByDescending按降序排序。如果第一次排序的结果很类似,就可以使用ThenBy和ThenByDescending操作符进行第二次排序。Reverse反转集合中元素的顺序


Join、GroupJoin:

连接运算符用于合并不直接相关的集合。使用Join操作符,可以根据键选择器函连接两个集合,这类似于SQL中的Join。GroupJoin操作符连接两个集合,组合其结果


GroupBy、ToLooKup:

组合运算符把数据放在组中。GroupBy操作符组合有公共键的元素。ToLoopup通过创建一个一对多的字典、来组合元素。


Any、All、Contains:

如果元素序列满足指定的条件,量词操作符就返回布尔值。Any、All和Contains都是限定符操作符。Any确定集合中是否满足谓词函数的元素;All确定集合中的所有元素是否都满足谓词函数;Contains检查某个元素是否存在集合中。这些操作符都返回一个布尔值。


Take、Skip、TakeWhile、SkipWhile:

分区操作符返回集合的一个子集。Take、Skip、TakeWhile和SkipWhile都是分区操作符。使用Take必须指定要从集合中提取的元素个数;Skip跳过指定的元素个数,提取元素;TakeWhile提取条件为真的元素。


Distinct、Union、Intersect、Except、Zip:

Set操作符返回一个集合。Distinct从集合中删除重复的元素。除了Distinct之外,其他Set操作符都需要两个集合。Union(并集)返回出现在其中一个集合的唯一元素。Intersect(交集)返回两个集合都有的元素。Except(差集)返回只出现一个集合中的元素。Zip是.Net新增的。它把两个集合合并为一个。


First、FirstOrDefault、Last、LastOrDefault、ElementAt、ElementAtOrDefault、Single、SingleOrDefault:

这些元素操作符仅返回一个元素。First返回第一个满足的元素。FirstOrDefault类似于First,但如果没有找到满足条件的元素,就返回类型的默认值。Last返回最后一个满足条件的元素。ElementAt指定了要返回的元素的位置。Single只返回一个满足条件的元素。如果多个元素都满足条件,就抛出一个异常。


Count、Sum、Min、Max、Average、Aggregate:

聚合操作符计算集合的一个值。利用这些聚合操作符,可以计算所有值得总和、所有元素的个数、值最大和最小的元素,以及平均值等。


ToArray、ToEnumerable、ToList、ToDictionary、Cast<TResult>:

这些转换操作符将集合转换为数组:IEnumerable、IList、IDictionary等。


Empty、Range、Repeat:

这些生成操作符返回一个新集合。使用Empty时集合是空的;Range返回一系列数字;Repeat返回一个始终重复一个值的集合。


Enumerable类的Concat方法用来连接连个序列,并返回由两个数组中所有元素组成的新序列。

语法格式:


public static IEnumerable<TSource> Concat<TSource>(this IEnumerable<TSource> first ,IEnumerable<TSource> second)


first:要连接的第一个序列 second:要与第一序列连接的序列。


返回值:一个IEnumerable<T>,包含连个输入序列的元素。


Let字句:

在查询表达式,存储表达式的结果有时很有用,这样可以在随后的字句中使用。


可以使用Let关键字完成这一工作,该关键字可以创建一个新的范围变量,并且用你提供的表达式的结果初始化改变量。一旦用值初始化了该范围变量,它就不能用于存储其他值。但如果该范围变量存储的是可查询的类型,则可以对其进行查询。


应用举例:


 数据部分:


 [Serializable]


   public class Team


   {

       public Team(string name, params int[] years)


       {

           this.Name = name;


           this.Years = years;


       }


       public string Name { get; private set; }


       public int[] Years { get; private set; }


   }


}


  [Serializable]


   public class Racer : IComparable<Racer>, IFormattable


   {

       public Racer(string firstName = null, string lastName = null, string country = null, int starts = 0, int wins = 0, IEnumerable<int> years = null, IEnumerable<string> cars = null)


       {

           this.FirstName = firstName;


           this.LastName = lastName;


           this.Country = country;


           this.Starts = starts;


           this.Wins = wins;


           var yearsList = new List<int>();


           foreach (var year in years)


           {

               yearsList.Add(year);


           }


           this.Years = yearsList.ToArray();


           var carList = new List<string>();


           foreach (var car in cars)


           {

               carList.Add(car);


           }


           this.Cars = carList.ToArray();


       }


       public string FirstName { get; set; }


       public string LastName { get; set; }


       public string Country { get; set; }


       public int Wins { get; set; }


       public int Starts { get; set; }


       public string[] Cars { get; private set; }


       public int[] Years { get; private set; }


       public override string ToString()


       {

           return String.Format("{0} {1}", FirstName, LastName);


       }


       public int CompareTo(Racer other)


       {

           if (other == null) throw new ArgumentNullException("other");



           return this.LastName.CompareTo(other.LastName);


       }


       public string ToString(string format)


       {

           return ToString(format, null);


       }


       public string ToString(string format,


             IFormatProvider formatProvider)


       {

           switch (format)


           {

               case null:


               case "N":


                   return ToString();


               case "F":


                   return FirstName;


               case "L":


                   return LastName;


               case "C":


                   return Country;


               case "S":


                   return Starts.ToString();


               case "W":


                   return Wins.ToString();


               case "A":


                   return String.Format("{0} {1}, {2}; starts: {3}, wins: {4}",


                         FirstName, LastName, Country, Starts, Wins);


               default:


                   throw new FormatException(String.Format("Format {0} not supported", format));


           }


       }


   }



public static class Formula1


   {


       private static List<Racer> racers;


       public static IList<Racer> GetChampions()


       {


           if (racers == null)


           {


               racers = new List<Racer>(40);


               racers.Add(new Racer("Nino", "Farina", "Italy", 33, 5, new int[] { 1950 }, new string[] { "Alfa Romeo" }));


               racers.Add(new Racer("Alberto", "Ascari", "Italy", 32, 10, new int[] { 1952, 1953 }, new string[] { "Ferrari" }));


               racers.Add(new Racer("Juan Manuel", "Fangio", "Argentina", 51, 24, new int[] { 1951, 1954, 1955, 1956, 1957 }, new string[] { "Alfa Romeo", "Maserati", "Mercedes", "Ferrari" }));


               racers.Add(new Racer("Mike", "Hawthorn", "UK", 45, 3, new int[] { 1958 }, new string[] { "Ferrari" }));


               racers.Add(new Racer("Phil", "Hill", "USA", 48, 3, new int[] { 1961 }, new string[] { "Ferrari" }));


               racers.Add(new Racer("John", "Surtees", "UK", 111, 6, new int[] { 1964 }, new string[] { "Ferrari" }));


               racers.Add(new Racer("Jim", "Clark", "UK", 72, 25, new int[] { 1963, 1965 }, new string[] { "Lotus" }));


               racers.Add(new Racer("Jack", "Brabham", "Australia", 125, 14, new int[] { 1959, 1960, 1966 }, new string[] { "Cooper", "Brabham" }));


               racers.Add(new Racer("Denny", "Hulme", "New Zealand", 112, 8, new int[] { 1967 }, new string[] { "Brabham" }));


               racers.Add(new Racer("Graham", "Hill", "UK", 176, 14, new int[] { 1962, 1968 }, new string[] { "BRM", "Lotus" }));


               racers.Add(new Racer("Jochen", "Rindt", "Austria", 60, 6, new int[] { 1970 }, new string[] { "Lotus" }));


               racers.Add(new Racer("Jackie", "Stewart", "UK", 99, 27, new int[] { 1969, 1971, 1973 }, new string[] { "Matra", "Tyrrell" }));


               racers.Add(new Racer("Emerson", "Fittipaldi", "Brazil", 143, 14, new int[] { 1972, 1974 }, new string[] { "Lotus", "McLaren" }));


               racers.Add(new Racer("James", "Hunt", "UK", 91, 10, new int[] { 1976 }, new string[] { "McLaren" }));


               racers.Add(new Racer("Mario", "Andretti", "USA", 128, 12, new int[] { 1978 }, new string[] { "Lotus" }));


               racers.Add(new Racer("Jody", "Scheckter", "South Africa", 112, 10, new int[] { 1979 }, new string[] { "Ferrari" }));


               racers.Add(new Racer("Alan", "Jones", "Australia", 115, 12, new int[] { 1980 }, new string[] { "Williams" }));


               racers.Add(new Racer("Keke", "Rosberg", "Finland", 114, 5, new int[] { 1982 }, new string[] { "Williams" }));


               racers.Add(new Racer("Niki", "Lauda", "Austria", 173, 25, new int[] { 1975, 1977, 1984 }, new string[] { "Ferrari", "McLaren" }));


               racers.Add(new Racer("Nelson", "Piquet", "Brazil", 204, 23, new int[] { 1981, 1983, 1987 }, new string[] { "Brabham", "Williams" }));


               racers.Add(new Racer("Ayrton", "Senna", "Brazil", 161, 41, new int[] { 1988, 1990, 1991 }, new string[] { "McLaren" }));


               racers.Add(new Racer("Nigel", "Mansell", "UK", 187, 31, new int[] { 1992 }, new string[] { "Williams" }));


               racers.Add(new Racer("Alain", "Prost", "France", 197, 51, new int[] { 1985, 1986, 1989, 1993 }, new string[] { "McLaren", "Williams" }));


               racers.Add(new Racer("Damon", "Hill", "UK", 114, 22, new int[] { 1996 }, new string[] { "Williams" }));


               racers.Add(new Racer("Jacques", "Villeneuve", "Canada", 165, 11, new int[] { 1997 }, new string[] { "Williams" }));


               racers.Add(new Racer("Mika", "Hakkinen", "Finland", 160, 20, new int[] { 1998, 1999 }, new string[] { "McLaren" }));


               racers.Add(new Racer("Michael", "Schumacher", "Germany", 250, 91, new int[] { 1994, 1995, 2000, 2001, 2002, 2003, 2004 }, new string[] { "Benetton", "Ferrari" }));


               racers.Add(new Racer("Fernando", "Alonso", "Spain", 132, 21, new int[] { 2005, 2006 }, new string[] { "Renault" }));


               racers.Add(new Racer("Kimi", "Räikkönen", "Finland", 148, 17, new int[] { 2007 }, new string[] { "Ferrari" }));


               racers.Add(new Racer("Lewis", "Hamilton", "UK", 44, 9, new int[] { 2008 }, new string[] { "McLaren" }));


           }


           return racers;


       }


  private static List<Team> teams;


       public static IList<Team> GetContructorChampions()


       {

           if (teams == null)


           {

               teams = new List<Team>()


               {

                   new Team("Vanwall", 1958),


                   new Team("Cooper", 1959, 1960),


                   new Team("Ferrari", 1961, 1964, 1975, 1976, 1977, 1979, 1982, 1983, 1999, 2000, 2001, 2002, 2003, 2004, 2007, 2008),


                   new Team("BRM", 1962),


                   new Team("Lotus", 1963, 1965, 1968, 1970, 1972, 1973, 1978),


                   new Team("Brabham", 1966, 1967),


                   new Team("Matra", 1969),


                   new Team("Tyrrell", 1971),


                   new Team("McLaren", 1974, 1984, 1985, 1988, 1989, 1990, 1991, 1998),


                   new Team("Williams", 1980, 1981, 1986, 1987, 1992, 1993, 1994, 1996, 1997),


                   new Team("Benetton", 1995),


                   new Team("Renault", 2005, 2006 )


               };


           }


           return teams;


       }


}


筛选:

使用Where字句,可以合并多个表达式。例如,找出赢得至少15场的巴西和奥地利赛手


使用Linq语句:

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


    }


结果: Niki Lauda, Austria; starts: 173, wins: 25


Nelson Piquet, Brazil; starts: 204, wins: 23


Ayrton Senna, Brazil; starts: 161, wins: 41


使用扩展方法

第一种写法:  var racers = Formula1.GetChampions().Where(r => r.Wins > 15 && (r.Country == "Brazil" || r.Country == "Austria")).Select(r=>r);


第二种写法:  var racers = Formula1.GetChampions().Where(r => r.Wins > 15 && (r.Country == "Brazil" || r.Country == "Austria"));


    foreach (var r in racers)


      {

        Console.WriteLine("{0:A}", r);


          }


结果: Niki Lauda, Austria; starts: 173, wins: 25


Nelson Piquet, Brazil; starts: 204, wins: 23


Ayrton Senna, Brazil; starts: 161, wins: 41


用索引筛选:下面由Where扩展方法调用,他使用索引返回姓氏以A开头,索引为偶数的赛手。

 var racers =Formula1.GetChampions().Where((r, index) => r.LastName.StartsWith("A") && index % 2 != 0);


    foreach (var r in racers)


    {

        Console.WriteLine("{0:A}", r);


         }


结果:Alberto Ascari, Italy; starts: 32, wins: 10


Fernando Alonso, Spain; starts: 132, wins: 21


类型筛选:

使用OfType()扩展方法,把string类传给泛型参数,就从集合中仅返回字符串:


object[] data = { "one", 2, 3, "four", "five", 6 };


           var query = data.OfType<string>();



结果:


one


four


five


           foreach (var s in query)



           {

               Console.WriteLine(s);


               }


符合的from字句

Racer类定义了属性Cars,其中Cars是一个字符串数组。要筛选驾驶法拉利的所有冠军。


第一个from字句访问从Formula1.GetChampions()方法返回的Racer对象,第二个from字句访问Racer类的Cars属性,以返回所有string类型的赛车。接着在Where字句中使用这些赛车筛选驾驶法拉利的所有冠军。


Linq语句写法:

  var ferrariDrivers = from r in Formula1.GetChampions()


                                from c in r.Cars


                                where c == "Ferrari"


                                orderby r.LastName


                                select r.FirstName + " " + r.LastName;


扩展方法写法:

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


遍历取出来的结果:

           foreach (var racer in ferrariDrivers)


           {

               Console.WriteLine(racer);


               }


结果:Alberto Ascari


Juan Manuel Fangio


Mike Hawthorn


Phil Hill


Niki Lauda


Kimi R?ikk?nen


Jody Scheckter


Michael Schumacher


John Surtees


符合的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);


第一个参数是隐式参数,它从GetChampions()方法中接收Racer对象序列。第二个参数是CollectionSelector委托,其中定义了内部序列。在Lambda表达式r=>r.Cars


中,应返回赛车集合。第三个参数是一个委托,现在每个赛车调用该委托,它有Racer和Car属性。这个SelectMany()方法的结果是摊平了赛手和赛车的层次结构,为每辆赛车返回匿名类型的一个新对象集合。


          把SelectMany()泛型方法解析为这里的使用类型;数据源是Racer类型,所筛选的集合是一个string数组,当返回的是匿名类型的名称是未知的,这里显示为TResult;


       public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<Racer> source,


Func<Racer, IEnumerable<string>> collectionSelector,


Func<Racer, string, TResult> resultSelector);


排序:

Linq写法:

var query = from r in Formula1.GetChampions()


                       where r.Country == "Brazil"


                       orderby r.Wins descending


                       select r;


扩展方法:

          第一种:query= Formula1.GetChampions().Where(r => r.Country == "Brazil").OrderByDescending(r => r.Wins).Select(r=>r);


第二种:var query= Formula1.GetChampions().Where(r => r.Country == "Brazil").OrderByDescending(r => r.Wins);


遍历取出来的结果:foreach (var r in query)

                 {

                  Console.WriteLine("{0:A}", r);


                     }



结果:Ayrton Senna, Brazil; starts: 161, wins: 41


          elson Piquet, Brazil; starts: 204, wins: 23


          merson Fittipaldi, Brazil; starts: 143, wins: 14


OrderBy和OrderByDescending()方法返回IOrderEnumerable<TSource>。这个接口派生自IEnumerable<TSource>(),这个方法用于进一步给序列排序。如果根据关键字选择器来排序,其中两项相同,就可以使用ThenBy()和ThenByDescending()方法继续排序。这两个方法需要IOrderEnumerable<TSource>接口才能工作,但也返回这个接口。所以可以添加任意多个ThenBy()和ThenByDescending()方法,对集合排序。


  var  racers=(from r in Formula1.GetChampions() orderby r.Country,r.LastName,r.FirstName select r).Take(10);//Take(10)扩展方法用于提取前10个结果。


使用扩展方法:


  var racers= Formula1.GetChampions().OrderBy(r=>r.Country).ThenBy(r.LastName).ThenBy(r=>r.FirstName).Take(10);


分组:

Linq的写法:

使用Group字句按照国家分组,并列出一个国家的冠军数 Group r by r.Country into g 根据Country属性组合所有赛手,并的那个一个新的标识符 g,它以后用于访问分组的结果信息。Group 字句的结果根据应用分组结果上的扩展方法Count()来排序,如果冠军数相同,就根据关键字来排序,该关键字就是国家,因为这是分组所使用的关键字。Where字句根据至少有两项的分组来筛选结果,Select字句创建一个带Country和count属性的匿名类型。


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


                           };          


使用扩展方法:要用扩展方法执行相同的操作应把GroupBy字句解析为GroupBy()方法。在GroupBy()方法的声明中,注意它返回实现了IGrouping接口的对象。IGrouping接口定义了Key属性,所以在定义了对这个方法的调用后,可以访问分组的关键字。

       public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector);


把字句group r by r.Country into g 解析为GroupBy(r=>r.Country),返回分组序列。分组序列首先用OrderByDescending()方法排序,再用ThenBy()方法排序。接着调用Where()和Select()方法。


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


           });


遍历取出来的结果:foreach (var item in countries)

            {

               Console.WriteLine("{0, -10} {1}", item.Country, item.Count);


                }


结果:UK         9


   Brazil       3


          Finland      3


          Australia     2


          Austria       2


          Italy         2


          USA         2


对嵌套的对象分组:

如果分组的对象应包含嵌套的序列,就可以改变Select字句创建的匿名类型。在下面的例子中,所返回的国家不仅包含国家名和赛手数量这两个属性,还包含赛手名序列。这个序列用一个赋予Racers属性的from/in内部字句指定,内部的from字句使用分组标识符g获得该分组中的所有赛手,用姓氏对他们排序,在根据姓名创建一个新的字符串。


Linq的写法:

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


                           };


扩展方法的写法:

  var countries = Formula1.GetChampions().GroupBy(r => r.Country).OrderByDescending(r => r.Count()).ThenBy(r => r.Key).Where(r => r.Count() >= 2).Select(r =>


                      new


                      {

                          Country = r.Key,


                          Count = r.Count(),


                          Racers = r.OrderBy(g => g.LastName).Select(g => g.FirstName + " " + g.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();


           }


结果:


UK         9


Jim Clark; Lewis Hamilton; Mike Hawthorn; Graham Hill; Damon Hill; James Hunt; N


igel Mansell; Jackie Stewart; John Surtees;


Brazil     3


Emerson Fittipaldi; Nelson Piquet; Ayrton Senna;


Finland    3


Mika Hakkinen; Kimi R?ikk?nen; Keke Rosberg;


Australia  2


Jack Brabham; Alan Jones;


Austria    2


Niki Lauda; Jochen Rindt;


Italy      2


Alberto Ascari; Nino Farina;


USA        2


Mario Andretti; Phil Hill;


连接:

使用join字句可以根据特定的条件合并两个数据源,但之前要获得两个要连接的列表。在一级方程式比赛中,有赛手冠军和车队冠军。赛手从GetContructorChampions ()方法中返回,现在要获得一个年份列表,列出赛手冠军和车队冠军。为此,先定义两个查询,用于查询赛车和车队。


var racers = from r in Formula1.GetChampions()


                        from y in r.Years


                        where y > 2003


                        select new


                        {

                            Year = y,


                            Name = r.FirstName + " " + r.LastName


                        };


    var teams = from t in


                           Formula1.GetContructorChampions()


                       from y in t.Years


                       where y > 2003


                       select new


                       {

                           Year = y,


                           Name = t.Name


                       };


有了这两个查询,再通过字句join r in teams on r.Year equals t.Year,根据赛手获得冠军的年份和车队获得冠军的年份进行连接。Select字句定义了一个新的匿名类型,它包含Year、Racer和Team属性


var racersAndTeams =


                 from r in racers


                 join t in teams on r.Year equals t.Year


                 select new


                 {

                     Year = r.Year,


                     Racer = r.Name,


                     Team = t.Name


                 };


把上述合并成一个Linq查询语句


           var racersAndTeams = from r in


from r1 in Formula1.GetChampions() from yr in r1.Years


                               where yr > 2003


                                 select new


                                 {

                                           Year = yr,


                                    Name = r1.FirstName + " " + r1.LastName


                                 }


                                join t in


from t1 in Formula1.GetContructorChampions()  from yt in t1.Years


                                where yt > 2003


                                select new


                                 {

                                      Year = yt,


                                      Name = t1.Name


                                 }


                               on r.Year equals t.Year


                                select new


                                {

                                    Year=r.Year,


                                    Racer=r.Name,


                                    Team=t.Name


                                };          


Console.WriteLine("Year  Champion " + "Constructor Title");


           foreach (var item in racersAndTeams)


           {

               Console.WriteLine("{0}: {1,-20} {2}",


                  item.Year, item.Racer, item.Team);


           }


结果:


Year  Champion Constructor Title


2004: Michael Schumacher   Ferrari


2005: Fernando Alonso      Renault


2006: Fernando Alonso      Renault


2007: Kimi R?ikk?nen       Ferrari


2008: Lewis Hamilton       Ferrari


集合操作:

扩展方法Distinct()、Union()、Intersect()和Except()都是集合操作。下面创建一个驾驶法拉利的以及方程式冠军序列和驾驶迈凯轮的一级方程式冠军序列,然后确定是否有驾驶法拉利和迈凯轮的冠军。当然可以使用Intersect()扩展方法。

获取所有驾驶法拉利的冠军:

var ferrariDrivers = from r in Formula1.GetChampions()


                               from c in r.Cars


                               where c == "Ferrari"


                               orderby r.LastName


                                   select r;


写一个公共的方法来获取所有驾驶迈凯轮的冠军

private static IEnumerable<Racer> GetRacerByCar(string car)


{

          return from r in Formula1.GetChampions() from c in r.Cars where c==car orderby r.LastName select r;


}


             因为不在其他的地方使用,所以定义一个委托类型的变量来保存Linq查询。racerByCar变量必须是一个委托类型,该委托类型需要一个字符串参数,并返回IEnumerable<Racer>,类似前面实现的方法。为此,定义了几个泛型委托Fun<>,所以不需要声明自己的委托。把一个Lambda表达赋予racerByCar变量。Lambda表达式的左边定义了一个car的变量,其类型是Func委托(字符串)的第一个泛型参数。右边定义了Linq查询,它使用该参数和Where字句:


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


           foreach (var racer in racersByCar("Ferrari").Intersect(racersByCar("McLaren")))


           {

               Console.WriteLine(racer);


               }


结果:


World champion with Ferrari and McLaren


Niki Lauda



合并

                     Zip()方法是.Net新增的,允许用一个谓词函数把两个相关的序列合并一个。首先创建两个相关的序列,他们使用相同的筛选和排序方法。对于合并,这个很重要,因为第一个集合中的第一项会与第二个集合中的第一项合并,第一个集合中的第二项与第二个集合中的第二项合并,以此类推。如果两个序列不相同,Zip()就在较小的集合的末尾时停止。


               第一个集合中的元素有一个Name属性,第二个集合元素有LastName和Starts两个属性。


               在racerName是集合上使用Zip()方法,需要把第二个集合(racerNameAndStarts)作为第一个参数。第二个参数的类型是Func<TFirst,TSecond,TResult>。这个参数实现为一个Lambda表达式,它通过参数first接收第一个集合的元素,通过参数second接收第二个集合元素,其实现代码创建并返回一个字符串,该字符串包含第一个集合中元素的Name属性和第二个集合中元素的Starts属性:


 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


                                  };



结果:


Alberto Ascari, starts: 32


Nino Farina, starts: 33


           var racers = racerNames.Zip(racerNamesAndStarts, (first, second) => first.Name + ", starts: " + second.Starts);      



           foreach (var r in racers)


           {

               Console.WriteLine(r);


               }


分区

         扩展方法Take()和Skip()等的分区操作可用于分页,例如显示5×5赛手


例:Skip方法先忽略根据页面大小和实际页数计算出的项数,在使用Take()方法根据页面大小提取一定数量的项;


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


                   select r.FirstName + " " + r.LastName).


                  Skip(page * pageSize).Take(pageSize);


               foreach (var name in racers)


               {

                   Console.WriteLine(name);


               }


               Console.WriteLine();


           }


结果:


Page 0


Fernando Alonso


Mario Andretti


Alberto Ascari


Jack Brabham


Jim Clark



Page 1


Juan Manuel Fangio


Nino Farina


Emerson Fittipaldi


Mika Hakkinen


Lewis Hamilton



Page 2


Mike Hawthorn


Phil Hill


Graham Hill


Damon Hill


Denny Hulme



Page 3


James Hunt


Alan Jones


Niki Lauda


Nigel Mansell


Nelson Piquet



Page 4


Alain Prost


Kimi R?ikk?nen


Jochen Rindt


Keke Rosberg


Jody Scheckter



Page 5


Michael Schumacher


Ayrton Senna


Jackie Stewart


John Surtees


Jacques Villeneuve


聚合操作符

  聚合操作符(如:count(),Sum(),Min(),Max(),Average()和Aggregate())返回的是一个值。


下面的Count()用于Racer的Years属性。返回冠军词素超过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 r in query)


           {

               Console.WriteLine("{0} {1}", r.Name, r.TimesChampion);


               }


结果:Michael Schumacher 7


Juan Manuel Fangio 5


Alain Prost 4


Sum()方法汇总序列中的所有数字,返回这些数字的和,下面是Sun()方法用于计算一个国家的赢得比赛的总次数。首先根据国家对赛手的分组,再在新创建的匿名类型中,把Wins属性赋予某个国家赢得的总次数。

var countries = (from c in  from r in Formula1.GetChampions() group r by r.Country into c   select new


           {

                Country = c.Key,


                Wins = (from r1 in c select r1.Wins).Sum()


           }


                            orderby c.Wins descending, c.Country


                            select c).Take(5);


           foreach (var country in countries)


           {

               Console.WriteLine("{0} {1}", country.Country, country.Wins);


           }


结果:UK 147


Germany 91


Brazil 78


France 51


Finland 42


转换:

    在迭代中使用查询时,查询就会执行。而使用转换操作符会立即执行查询,把查询结果放在数组列表或字典中。


例:调用ToList扩展方法,立即执行查询,得到的结果放在List<T>类中。


               List<Racer> racers=(from r in Formula1.GetChampions() where r.Starts>150 orderby r.Starts descending select r).ToList();


               Foreach( var racer in racers)


               {

                     Console.WriteLine(“{0}  {0:S}”,racer);


               }


          把返回的对象放在列表中并没有这么简单。例如,对于集合中从赛车到赛车手的快速访问,可以使用新类Lookup<TKey,TElement>。


          注:Dictionary<TKey,Tvalue>类只支持一个键对应一个值。在System.Linq命名空间的类Lookup<TKey,TElement>类中,一个键可以对应多个值。


使用符合的from查询,可以摊平赛手和赛车序列,创建带有Car和Racer属性的匿名类型。在返回的Lookup对象中,键的类型影视表示汽车的string,值的类型影视Racer。为了惊醒这个选择,可以给ToLookup()方法的重载版本传递一个键和一个元素选择器。键选择器引用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);


           if (racers.Contains("Williams"))


           {

               foreach (var williamsRacer in racers["Williams"])


               {

                   Console.WriteLine(williamsRacer);


               }


           }


结果:Alan Jones


Keke Rosberg


Nelson Piquet


Nigel Mansell


Alain Prost


Damon Hill


Jacques Villeneuve


如果需要在非类型化的集合上(如ArrayList),使用Linq查询,就可以用Cast()方法。例:基于Object类型的ArrayList集合用Racer对象填充。为了定义强类型的查询,可以使用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);


               }


输出结果:


Mario Andretti, USA; starts: 128, wins: 12


Phil Hill, USA; starts: 48, wins: 3


生成操作符。

                生成操作Range(),Empty()和Repear()不是扩展方法,而是返回序列的正常静态方法。在Linq to object中,这些方法用于Enumerable类。有时需要填充一个范围的数字,此时就应使用Range()方法。这个方法把第一个参数作为起始值,把第二个参数作为要填充的项数。


var values=Enumerable.Range(1,20);


foreach( var item in values)


{

           Console.Write(“{0}”,item);


}


结果:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20


注:Range() 方法不返回填充了所定义值的集合,这个方法与其他方法一样,也推迟执行查询,并返回一个RangeEnumerator,其中只有一条yield return语句,来递增。可以把该结果与扩展方法合并起来获得另一个结果,例:Select()扩展方法


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


Empty()方法返回一个不返回值的迭代器,它可以用于需要一个集合的参数,其中可以给参数穿空集合。


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


15、Linq 实现sql中的not in和in条件查询


1、in条件查询


var queryResult = from p in db.Products


where (new int?[] {1,2}).Contains(p.CategoryID)


select p;


2、not in条件查询


var queryResult = from p in db.Products


where ! (new int?[] {1,2}).Contains(p.CategoryID)


select p;


上一篇:一分钟了解阿里云产品:云数据库Redis版


下一篇:【晒出你的第83行代码】前端专家专门找出来珍藏的反面代码教材,请大家来拍砖!