Linq


1.Linq概述

列表和实体

准备数据:

public class Championship{public int Year { get; set; }public string First { get; set; }public string Second { get; set; }public string Third { get; set; }}

Championship

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", 287, 91, new int[] { 1994, 1995, 2000, 2001, 2002, 2003, 2004 }, new string[] { "Benetton", "Ferrari" }));racers.Add(new Racer("Fernando", "Alonso", "Spain", 177, 27, 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", 90, 17, new int[] { 2008 }, new string[] { "McLaren" }));racers.Add(new Racer("Jenson", "Button", "UK", 208, 12, new int[] { 2009 }, new string[] { "Brawn GP" }));racers.Add(new Racer("Sebastian", "Vettel", "Germany", 81, 21, new int[] { 2010, 2011 }, new string[] { "Red Bull Racing" }));}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 ),new Team("Brawn GP", 2009),new Team("Red Bull Racing", 2010, 2011)};}return teams;}private static List<Championship> championships;public static IEnumerable<Championship> GetChampionships(){if (championships == null){championships = new List<Championship>();championships.Add(new Championship{Year = 1950,First = "Nino Farina",Second = "Juan Manuel Fangio",Third = "Luigi Fagioli"});championships.Add(new Championship{Year = 1951,First = "Juan Manuel Fangio",Second = "Alberto Ascari",Third = "Froilan Gonzalez"});championships.Add(new Championship{Year = 1952,First = "Alberto Ascari",Second = "Nino Farina",Third = "Piero Taruffi"});championships.Add(new Championship{Year = 1953,First = "Alberto Ascari",Second = "Juan Manuel Fangio",Third = "Nino Farina"});championships.Add(new Championship{Year = 1954,First = "Juan Manuel Fangio",Second = "Froilan Gonzalez",Third = "Mike Hawthorn"});championships.Add(new Championship{Year = 1955,First = "Juan Manuel Fangio",Second = "Stirling Moss",Third = "Eugenio Castellotti"});championships.Add(new Championship{Year = 1956,First = "Juan Manuel Fangio",Second = "Stirling Moss",Third = "Peter Collins"});championships.Add(new Championship{Year = 1957,First = "Juan Manuel Fangio",Second = "Stirling Moss",Third = "Luigi Musso"});championships.Add(new Championship{Year = 1958,First = "Mike Hawthorn",Second = "Stirling Moss",Third = "Tony Brooks"});championships.Add(new Championship{Year = 1959,First = "Jack Brabham",Second = "Tony Brooks",Third = "Stirling Moss"});championships.Add(new Championship{Year = 1960,First = "Jack Brabham",Second = "Bruce McLaren",Third = "Stirling Moss"});championships.Add(new Championship{Year = 1961,First = "Phil Hill",Second = "Wolfgang von Trips",Third = "Stirling Moss"});championships.Add(new Championship{Year = 1962,First = "Graham Hill",Second = "Jim Clark",Third = "Bruce McLaren"});championships.Add(new Championship{Year = 1963,First = "Jim Clark",Second = "Graham Hill",Third = "Richie Ginther"});championships.Add(new Championship{Year = 1964,First = "John Surtees",Second = "Graham Hill",Third = "Jim Clark"});championships.Add(new Championship{Year = 1965,First = "Jim Clark",Second = "Graham Hill",Third = "Jackie Stewart"});championships.Add(new Championship{Year = 1966,First = "Jack Brabham",Second = "John Surtees",Third = "Jochen Rindt"});championships.Add(new Championship{Year = 1967,First = "Dennis Hulme",Second = "Jack Brabham",Third = "Jim Clark"});championships.Add(new Championship{Year = 1968,First = "Graham Hill",Second = "Jackie Stewart",Third = "Dennis Hulme"});championships.Add(new Championship{Year = 1969,First = "Jackie Stewart",Second = "Jackie Ickx",Third = "Bruce McLaren"});championships.Add(new Championship{Year = 1970,First = "Jochen Rindt",Second = "Jackie Ickx",Third = "Clay Regazzoni"});championships.Add(new Championship{Year = 1971,First = "Jackie Stewart",Second = "Ronnie Peterson",Third = "Francois Cevert"});championships.Add(new Championship{Year = 1972,First = "Emerson Fittipaldi",Second = "Jackie Stewart",Third = "Dennis Hulme"});championships.Add(new Championship{Year = 1973,First = "Jackie Stewart",Second = "Emerson Fittipaldi",Third = "Ronnie Peterson"});championships.Add(new Championship{Year = 1974,First = "Emerson Fittipaldi",Second = "Clay Regazzoni",Third = "Jody Scheckter"});championships.Add(new Championship{Year = 1975,First = "Niki Lauda",Second = "Emerson Fittipaldi",Third = "Carlos Reutemann"});championships.Add(new Championship{Year = 1976,First = "James Hunt",Second = "Niki Lauda",Third = "Jody Scheckter"});championships.Add(new Championship{Year = 1977,First = "Niki Lauda",Second = "Jody Scheckter",Third = "Mario Andretti"});championships.Add(new Championship{Year = 1978,First = "Mario Andretti",Second = "Ronnie Peterson",Third = "Carlos Reutemann"});championships.Add(new Championship{Year = 1979,First = "Jody Scheckter",Second = "Gilles Villeneuve",Third = "Alan Jones"});championships.Add(new Championship{Year = 1980,First = "Alan Jones",Second = "Nelson Piquet",Third = "Carlos Reutemann"});championships.Add(new Championship{Year = 1981,First = "Nelson Piquet",Second = "Carlos Reutemann",Third = "Alan Jones"});championships.Add(new Championship{Year = 1982,First = "Keke Rosberg",Second = "Didier Pironi",Third = "John Watson"});championships.Add(new Championship{Year = 1983,First = "Nelson Piquet",Second = "Alain Prost",Third = "Rene Arnoux"});championships.Add(new Championship{Year = 1984,First = "Niki Lauda",Second = "Alain Prost",Third = "Elio de Angelis"});championships.Add(new Championship{Year = 1985,First = "Alain Prost",Second = "Michele Alboreto",Third = "Keke Rosberg"});championships.Add(new Championship{Year = 1986,First = "Alain Prost",Second = "Nigel Mansell",Third = "Nelson Piquet"});championships.Add(new Championship{Year = 1987,First = "Nelson Piquet",Second = "Nigel Mansell",Third = "Ayrton Senna"});championships.Add(new Championship{Year = 1988,First = "Ayrton Senna",Second = "Alain Prost",Third = "Gerhard Berger"});championships.Add(new Championship{Year = 1989,First = "Alain Prost",Second = "Ayrton Senna",Third = "Riccardo Patrese"});championships.Add(new Championship{Year = 1990,First = "Ayrton Senna",Second = "Alain Prost",Third = "Nelson Piquet"});championships.Add(new Championship{Year = 1991,First = "Ayrton Senna",Second = "Nigel Mansell",Third = "Riccardo Patrese"});championships.Add(new Championship{Year = 1992,First = "Nigel Mansell",Second = "Riccardo Patrese",Third = "Michael Schumacher"});championships.Add(new Championship{Year = 1993,First = "Alain Prost",Second = "Ayrton Senna",Third = "Damon Hill"});championships.Add(new Championship{Year = 1994,First = "Michael Schumacher",Second = "Damon Hill",Third = "Gerhard Berger"});championships.Add(new Championship{Year = 1995,First = "Michael Schumacher",Second = "Damon Hill",Third = "David Coulthard"});championships.Add(new Championship{Year = 1996,First = "Damon Hill",Second = "Jacques Villeneuve",Third = "Michael Schumacher"});championships.Add(new Championship{Year = 1997,First = "Jacques Villeneuve",Second = "Heinz-Harald Frentzen",Third = "David Coulthard"});championships.Add(new Championship{Year = 1998,First = "Mika Hakkinen",Second = "Michael Schumacher",Third = "David Coulthard"});championships.Add(new Championship{Year = 1999,First = "Mika Hakkinen",Second = "Eddie Irvine",Third = "Heinz-Harald Frentzen"});championships.Add(new Championship{Year = 2000,First = "Michael Schumacher",Second = "Mika Hakkinen",Third = "David Coulthard"});championships.Add(new Championship{Year = 2001,First = "Michael Schumacher",Second = "David Coulthard",Third = "Rubens Barrichello"});championships.Add(new Championship{Year = 2002,First = "Michael Schumacher",Second = "Rubens Barrichello",Third = "Juan Pablo Montoya"});championships.Add(new Championship{Year = 2003,First = "Michael Schumacher",Second = "Kimi Räikkönen",Third = "Juan Pablo Montoya"});championships.Add(new Championship{Year = 2004,First = "Michael Schumacher",Second = "Rubens Barrichello",Third = "Jenson Button"});championships.Add(new Championship{Year = 2005,First = "Fernando Alonso",Second = "Kimi Räikkönen",Third = "Michael Schumacher"});championships.Add(new Championship{Year = 2006,First = "Fernando Alonso",Second = "Michael Schumacher",Third = "Felipe Massa"});championships.Add(new Championship{Year = 2007,First = "Kimi Räikkönen",Second = "Lewis Hamilton",Third = "Fernando Alonso"});championships.Add(new Championship{Year = 2008,First = "Lewis Hamilton",Second = "Felipe Massa",Third = "Kimi Raikkonen"});championships.Add(new Championship{Year = 2009,First = "Jenson Button",Second = "Sebastian Vettel",Third = "Rubens Barrichello"});championships.Add(new Championship{Year = 2010,First = "Sebastian Vettel",Second = "Fernando Alonso",Third = "Mark Webber"});championships.Add(new Championship{Year = 2011,First = "Sebastian Vettel",Second = "Jenson Button",Third = "Mark Webber"});}return championships;}private static IList<Racer> moreRacers;private static IList<Racer> GetMoreRacers(){if (moreRacers == null){moreRacers = new List<Racer>();moreRacers.Add(new Racer("Luigi", "Fagioli", "Italy", starts: 7, wins: 1));moreRacers.Add(new Racer("Jose Froilan", "Gonzalez", "Argentina", starts: 26, wins: 2));moreRacers.Add(new Racer("Piero", "Taruffi", "Italy", starts: 18, wins: 1));moreRacers.Add(new Racer("Stirling", "Moss", "UK", starts: 66, wins: 16));moreRacers.Add(new Racer("Eugenio", "Castellotti", "Italy", starts: 14, wins: 0));moreRacers.Add(new Racer("Peter", "Collins", "UK", starts: 32, wins: 3));moreRacers.Add(new Racer("Luigi", "Musso", "Italy", starts: 24, wins: 1));moreRacers.Add(new Racer("Tony", "Brooks", "UK", starts: 38, wins: 6));moreRacers.Add(new Racer("Bruce", "McLaren", "New Zealand", starts: 100, wins: 4));moreRacers.Add(new Racer("Wolfgang von", "Trips", "Germany", starts: 27, wins: 2));moreRacers.Add(new Racer("Richie", "Ginther", "USA", starts: 52, wins: 1));moreRacers.Add(new Racer("Jackie", "Ickx", "Belgium", starts: 116, wins: 8));moreRacers.Add(new Racer("Clay", "Regazzoni", "Switzerland", starts: 132, wins: 5));moreRacers.Add(new Racer("Ronnie", "Peterson", "Sweden", starts: 123, wins: 10));moreRacers.Add(new Racer("Francois", "Cevert", "France", starts: 46, wins: 1));moreRacers.Add(new Racer("Carlos", "Reutemann", "Argentina", starts: 146, wins: 12));moreRacers.Add(new Racer("Gilles", "Villeneuve", "Canada", starts: 67, wins: 6));moreRacers.Add(new Racer("Didier", "Pironi", "France", starts: 70, wins: 3));moreRacers.Add(new Racer("John", "Watson", "UK", starts: 152, wins: 5));moreRacers.Add(new Racer("Rene", "Arnoux", "France", starts: 149, wins: 7));moreRacers.Add(new Racer("Elio", "de Angelis", "Italy", starts: 108, wins: 2));moreRacers.Add(new Racer("Michele", "Alboreto", "Italy", starts: 194, wins: 5));moreRacers.Add(new Racer("Gerhard", "Berger", "Austria", starts: 210, wins: 10));moreRacers.Add(new Racer("Riccardo", "Patrese", "Italy", starts: 256, wins: 6));moreRacers.Add(new Racer("David", "Coulthard", "UK", starts: 246, wins: 13));moreRacers.Add(new Racer("Heinz-Harald", "Frentzen", "Germany", starts: 156, wins: 3));moreRacers.Add(new Racer("Eddie", "Irvine", "UK", starts: 147, wins: 4));moreRacers.Add(new Racer("Rubens", "Barrichello", "Brazil", starts: 322, wins: 11));moreRacers.Add(new Racer("Juan Pablo", "Montoya", "Columbia", starts: 94, wins: 7));moreRacers.Add(new Racer("Felipe", "Massa", "Brazil", starts: 152, wins: 11));moreRacers.Add(new Racer("Mark", "Webber", "Australia", starts: 176, wins: 7));}return moreRacers;}}

Formula1

[Serializable]public class Racer : IComparable<Racer>, IFormattable{public Racer(string firstName, string lastName, string country, int starts, int wins): this(firstName, lastName, country, starts, wins, null, null){}public Racer(string firstName, string lastName, string country, int starts, int wins, IEnumerable<int> years, IEnumerable<string> cars){this.FirstName = firstName;this.LastName = lastName;this.Country = country;this.Starts = starts;this.Wins = wins;this.Years = new List<int>(years);this.Cars = new List<string>(cars);}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 IEnumerable<string> Cars { get; private set; }public IEnumerable<int> Years { get; private set; }public override string ToString(){return String.Format("{0} {1}", FirstName, LastName);}public int CompareTo(Racer other){if (other == null) return -1;return string.Compare(this.LastName, 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));}}}

Racer

[Serializable]public class Team{public Team(string name, params int[] years){this.Name = name;this.Years = new List<int>(years);}public string Name { get; private set; }public IEnumerable<int> Years { get; private set; }}

Team

Linq查询

查询来自巴西所有冠军:

var query = from r in Formula1.GetChampions()where r.Country == "Brazil"orderby r.Wins descendingselect r;foreach (var r in query)
{Console.WriteLine("{0:A}", r);
}

查询结果:

Ayrton Senna, Brazil; starts: 161, wins: 41
Nelson Piquet, Brazil; starts: 204, wins: 23
Emerson Fittipaldi, Brazil; starts: 143, wins: 14

from where orderby select是linq查询中预定义的关键字.

查询语句以from开头以select或group子句结尾

扩展方法
public static class StringExtension
{public static void Foo(this string s){Console.WriteLine("Foo invoked for {0}",s);}
}

在Linq中,Linq为IEnumerable<T>接口提供了各种扩展方法,方便查询。

扩展方法必须在静态类中进行声明,定义一个静态方法,其中第一个参数定义了它扩展的类型。

对于string类,因为它是密封类,因此不能被继承,所以可以通过扩展方法来进行扩展string类。

我们扩展了string类,然后可以使用它的扩展方法:

"你好".Fo();  //Foo invoked for 你好

这种扩展方法并没有违法面向对象的原则,因为定义了它的方法,并不能访问它的私有成员.

定义Linq扩展方法的类是Enumerable,只要导入这个空间,就可以访问该类的作用域。也就是说凡是继承该类的成员都可以访问了。下面列举了Where()扩展方法的实现代码。
Where()扩展方法的this关键字类型是IEnumerable,然后Where方法就可以应用与实现Ienumerable接口的每个类型,Where()扩展方法的Func<T,bool>委托,引用类返回布尔值,参数类型为T的方法。主要是检查IEnumerable<T>源中的项是否包含在目标集合中。如果委托引用了该方法,yield return语句就将源中的项返回给目标.

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source,Func<TSource,bool> predicate)
{foreach(TSource item in source)if(predicate(item))yield return item;
}

最后当我们调用时,只要实现了IEnumerable接口的所有类都可以使用该扩展方法

static void ExtensionMethods(){var champions = new List<Racer>(Formula1.GetChampions());IEnumerable<Racer> brazilChampions =champions.Where(r => r.Country == "Brazil").OrderByDescending(r => r.Wins).Select(r => r);foreach (Racer r in brazilChampions){Console.WriteLine("{0:A}", r);}}

推迟查询的执行

在运行期间定义查询表达式时,查询不会运行,查询会在迭代数据项时运行

2.标准查询

筛选

使用where子句可以合并多个表达式

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

View Code

不是所有的查询都用linq语句,高级查询可以使用扩展方法

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

用索引筛选

在Where方法重载中,可以传递第2个参数——索引,扩展方法Where方法中,含有参数是Func<TSource, int, bool> predicate的委托,第2个类型是索引

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

类型筛选

OfType扩展方法基于类型的筛选,通过指定的类型返回

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

复合from子句

如果需要对对象的成员进行筛选,则需要使用复合from子句,这种方式类似于循环里面套循环

var ferrariDrivers = from r in Formula1.GetChampions()from c in r.Carswhere c == "Ferrari"orderby r.LastNameselect r.FirstName + " " + r.LastName;foreach (var racer in ferrariDrivers){Console.WriteLine(racer);}

View Code

C#把复合的from子句和linq查询转换为SelectMany扩展方法,SelectMany()方法可用于迭代序列的序列。

public static IEnumerable<TResult> SelectMany<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector);

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

排序

使用orderby子句对序列进行排序,OrderBy()和OrderByDescending方法返回IOderEnumerable<TSource>,linq排序时多个关键字,逗号隔开.

 var racers2 = from r in Formula1.GetChampions()where r.Country == "Brazil"orderby r.Wins descendingselect r;

var racers = (from r in Formula1.GetChampions()orderby r.Country, r.LastName, r.FirstNameselect r).Take(10);foreach (var racer in racers){Console.WriteLine("{0}: {1}, {2}", racer.Country, racer.LastName, racer.FirstName);}

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

使用ThenBy和ThenByDescending同样可以实现排序.Take方法提取了排序的前10个元素。

分组

要根据一个关键字值对查询结果分组,可以使用group子句。

var countries = from r in Formula1.GetChampions()group r by r.Country into gorderby g.Count() descending, g.Keywhere g.Count() >= 2select new{Country = g.Key,Count = g.Count()};foreach (var item in countries){Console.WriteLine("{0, -10} {1}", item.Country, item.Count);}

View Code

代码中的标识符g,用来访问分组的结果信息。

要用扩展方法执行相同的操作。把groupby 子句解析为GroupBy()方法.

var countries2 = 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() });

对嵌套的对象分组

如果分组的对象应包含嵌套的序列,就可以改变select子句的创建的匿名类型

var countries = from r in Formula1.GetChampions()group r by r.Country into gorderby g.Count() descending, g.Keywhere g.Count() >= 2select new{Country = g.Key,Count = g.Count(),Racers = from r1 in gorderby r1.LastNameselect 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();}

View Code

内连接

使用join子句可以根据特定的条件合并两个数据源,但之前要获得两个要连接的列表。

var racers = from r in Formula1.GetChampions()from y in r.Yearsselect new{Year = y,Name = r.FirstName + " " + r.LastName};var teams = from t in Formula1.GetContructorChampions()from y in t.Yearsselect new{Year = y,Name = t.Name};

var racersAndTeams =(from r in racersjoin t in teams on r.Year equals t.Yearorderby t.Yearselect new{Year = r.Year,Champion = r.Name,Constructor = t.Name}).Take(10);Console.WriteLine("Year  World Champion\t   Constructor Title");foreach (var item in racersAndTeams){Console.WriteLine("{0}: {1,-20} {2}",item.Year, item.Champion, item.Constructor);}

左外连接

左外连接返回左边序列中的全部元素,即使他们在右边的序列中没有匹配的元素,左外连接用join子句和DefaultIfEmpty方法定义

var racers = from r in Formula1.GetChampions()from y in r.Yearsselect new{Year = y,Name = r.FirstName + " " + r.LastName};var teams = from t in Formula1.GetContructorChampions()from y in t.Yearsselect new{Year = y,Name = t.Name};var racersAndTeams =(from r in racersjoin t in teams on r.Year equals t.Year into rtfrom t in rt.DefaultIfEmpty()orderby r.Yearselect new{Year = r.Year,Champion = r.Name,Constructor = t == null ? "no constructor championship" : t.Name}).Take(10);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);}

View Code

组连接

左外连接使用了组连接和into子句,它有一部分语法和组连接相同,只不过组连接不使用DefaultIfEmpty方法,使用组连接时,可以连接两个独立的序列,对于其中一个序列中的某个元素,另一个序列中存在对应的一个项列表。

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 onnew{FirstName = r.FirstName,LastName = r.LastName}equalsnew{FirstName = r2.FirstName,LastName = r2.LastName}into yearResultsselect 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);}}

View Code

集合操作

扩展方法Distinct()、Union()、Intersect()和Except()都是集合操作。

Func<string, IEnumerable<Racer>> racersByCar =car => from r in Formula1.GetChampions()from c in r.Carswhere c == carorderby r.LastNameselect r;Console.WriteLine("World champion with Ferrari and McLaren");foreach (var racer in racersByCar("Ferrari").Intersect(racersByCar("McLaren"))){Console.WriteLine(racer);}

合并

Zip方法是.NET4中的,允许把2个相关的序列合并为一个

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 nonChampions = racers.Select(r =>new{FirstName = r.FirstName,LastName = r.LastName}).Except(Formula1.GetChampions().Select(r =>new{FirstName = r.FirstName,LastName = r.LastName}));foreach (var r in nonChampions){Console.WriteLine("{0} {1}", r.FirstName, r.LastName);}

View Code

分区

扩展方法Take()和Skip()等可用于分页

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

View Code

聚合操作符

聚合操作符返回一个值(Count()、Sum()、Min()、Max()、Average()和Aggregate())

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.FirstNameselect r.FirstName + " " + r.LastName).Skip(page * pageSize).Take(pageSize);foreach (var name in racers){Console.WriteLine(name);}Console.WriteLine();}

View Code

var countries = (from c infrom r in Formula1.GetChampions()group r by r.Country into cselect new{Country = c.Key,Wins = (from r1 in cselect r1.Wins).Sum()}orderby c.Wins descending, c.Countryselect c).Take(5);foreach (var country in countries){Console.WriteLine("{0} {1}", country.Country, country.Wins);}

View Code

转换操作符

linq查询可以推迟到访问数据项时再执行。在迭代中使用查询时,查询会执行。使用转换操作符会立即执行查询。

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 descendingselect r;foreach (var racer in query){Console.WriteLine("{0:A}", racer);}

生成操作符

生成操作符Range()、Empty()和Repear()不是扩展方法。是返回序列的静态方法.

var values=Enumerable.Range(1,20);
foreach(var item in values)
{Console.Write("{0}",item);
}

输出1到20的20个数字。

3.并行Linq

Linq中的ParallelEnumerable类可以分解查询的工作。使其分布在多个线程上。ParallelEnumerable类的大多数扩展方法是ParallelQuery<TSource>类的扩展。

并行查询

对于ParallelEnumerable类,查询是分区的,以便多个线程可以同时处理该查询。集合可以分多个部分,其中每个部分由不同的线程处理,以筛选其余项,完成分区的工作后,就需要合并,获得所有部分的总和。

   static IEnumerable<int> SampleData(){const int arraySize = 100000000;var r = new Random();return Enumerable.Range(0, arraySize).Select(x => r.Next(140)).ToList();}static void IntroParallel(){var data = SampleData();var watch = new Stopwatch();watch.Start();var q1 = (from x in datawhere Math.Log(x) < 4select x).Average();watch.Stop();Console.WriteLine("sync {0}, result: {1}", watch.ElapsedMilliseconds, q1);watch.Reset();watch.Start();var q2 = (from x in Partitioner.Create(data).AsParallel()where Math.Log(x) < 4select x).Average();watch.Stop();Console.WriteLine("async {0}, result: {1}", watch.ElapsedMilliseconds, q2);}

View Code

分区器

可以调用WithExecutionMode()和WithDegreeOfParallelism()方法,来影响并行机制。对于WithExecutionMode()方法可以传递ParallelExecutionMode的一个Default值或者ForceParallelism值。默认情况下,并行Linq避免使用系统开销很高的并行机制。对于WithDegreeOfParallelism()方法可以传递一个整数值,以指定应并行运行的最大任务数。查询不应使用全部CPU

var res = (from x in data.AsParallel().WithCancellation(cts.Token)where Math.Log(x) < 4select x).Average();

取消

.NET提供了一个标准方式。来取消长时间运行的任务。要取消长时间运行的查询。可以给查询添加WithCancellation()方法,并传递一个CancellationToken令牌作为参数。CancellationToken令牌从CancellationTokenSource类中创建,该查询在单独的线程中运行。在该线程中。捕获一个异常。如果取消查询就触发这个异常。在主线程中,调用CancellationTokenSource类的Cancel()方法可以取消任务

 var cts = new CancellationTokenSource();Task.Factory.StartNew(() =>{try{var res = (from x in data.AsParallel().WithCancellation(cts.Token)where Math.Log(x) < 4select x).Average();Console.WriteLine("query finished, result: {0}", res);}catch (OperationCanceledException ex){Console.WriteLine(ex.Message);}});Console.WriteLine("query started");Console.Write("cancel? ");string input = Console.ReadLine();if (input.ToLower().Equals("y")){cts.Cancel();Console.WriteLine("sent a cancel");}

cts.Cancel();

4.表达式树

我个人觉得理解某种程序扩展是很有意义的。

比如:

学会ASP.NET WebForm的服务器控件,就学会了ASP.NET用户控件,

学会ASP.NET MVC的@Html的扩展,就学会了HTMLHelper的自定义扩展,

现在学会了Linq表达式,就得学会Linq表达式树的扩展.

表达式树用于表示针对数据源的结构化查询,这些数据源实现 IQueryable<T>

以下部分代码来自世界中心的专栏

首先我们看看这块代码:

ParameterExpression exp1 = Expression.Parameter(typeof(int), "a");
ParameterExpression exp2 = Expression.Parameter(typeof(int), "b");//表达式主体的构建
BinaryExpression exp = Expression.Add(exp1, exp2);
//表达式树的构建(如下定义,表达式的类型为Lambda
//lambda表达式的类型为Func<int, int, int>)
var lambda = Expression.Lambda<Func<int, int, int>>(exp, exp1, exp2);

Expression介绍

Expression.Parameter用于构建表达式树的参数.

Expression.Parameter(typeof(int), "a")构建了表达式树的参数a,数据类型为int
委托表达式:(a,b)=>a+b 中的参数a已经构建好了。
既然参数a构建好了,那么b就同样构建出来了,现在就有了下面的代码

ParameterExpression exp1 = Expression.Parameter(typeof(int), "a"); ParameterExpression exp2 = Expression.Parameter(typeof(int), "b");
构建了委托表达式:(a,b)=>a+b 中的参数(a,b)

接下来构建表达式a+b,即构建BinaryExpression 对象

BinaryExpression 表示具有二进制运算符的表达式。

BinaryExpression exp = Expression.Add(exp1, exp2);构建了表达式a+b

Expression.Lambda()方法用于构建lamdba表达式,构建lamdba表达式的方法很多。我们就先了解

var lambda = Expression.Lambda<Func<int, int, int>>(exp, exp1, exp2);

这是当前Lamdba方法的定义:

public static Expression<TDelegate> Lambda<TDelegate>(Expression body, params ParameterExpression[] parameters);

Expression是表达式树的主体,凡是继承该类的都可以使用,BinaryExpression 就是继承了该类。

params ParameterExpression[] parameters用于传递多个参数值。注意Lamdba方法的第一个参数是表达式主体,第2个是参数列表

这里再重复一下另外一个问题。

当一个方法的参数列表超过3个的时候,需要考虑使用params或者对象进行传递数据.通常一个方法参数列表超过3个,那么第4个参数类型应该为params object[] objs,第4个参数需要使用params

最后这块代码:

ParameterExpression exp1 = Expression.Parameter(typeof(int), "a");
ParameterExpression exp2 = Expression.Parameter(typeof(int), "b");//表达式主体的构建
BinaryExpression exp = Expression.Add(exp1, exp2);
//表达式树的构建(如下定义,表达式的类型为Lambda
//lambda表达式的类型为Func<int, int, int>)
var lambda = Expression.Lambda<Func<int, int, int>>(exp, exp1, exp2);

构建了(a,b)=>a+b表达式,表达式返回类型为:Func<int,int,int>,这个由调用Lambda方法的类型约束决定。

接下来,考考你吧:

ParameterExpression exp3 = Expression.Parameter(typeof(Person), "p");
var property = Expression.Property(exp3, "Name");
var lambda2 = Expression.Lambda<Func<Person, string>>(property, exp3);

这块代码构建了什么表达式:

Expression.Property(exp3, "Name")顾名思义就是给参数p提供了一个属性Name;

那么构建后的表达式应该是:

p=>p.Name

View Code

接下来看看Where扩展方法中的表达式构建:

ParameterExpression exp3 = Expression.Parameter(typeof(Person), "p");
var property = Expression.Property(exp3, "Name");
var lambda2 = Expression.Lambda<Func<Person, string>>(property, exp3);//p=>p.Name == "daisy"
List<Person> persons = new List<Person>()
{ new Person(){ Name = "daisy", age = 10 },new Person(){ Name = "daisy", age = 12 },new Person(){Name="dom", age=12},new Person(){Name="caren", age=10}};var compareExp = simpleCompare<Person>("Name", "daisy");var daisys = persons.Where(compareExp).ToList();foreach (var item in daisys){Console.WriteLine("Name:  " + item.Name + "    Age:  " + item.age);}Console.ReadKey();

public static Func<TSource, bool> simpleCompare<TSource>(string property, object value)
{var type = typeof(TSource); //解析传入的对象类型,通过该类型指定表达式的参数类型为该对象类型var pe = Expression.Parameter(type, "p");//构建表达式参数var propertyReference = Expression.Property(pe, property);//构建表达式的对象属性,属性名为valuevar constantReference = Expression.Constant(value);//构建一个常数,该常数不是表达式中的参数var binaryExpression = Expression.Equal(propertyReference, constantReference);//构建匹配的表达式 p.Name==value//compile 是表达式的一个接口,生成该lambda表达式树对的委托  var res = Expression.Lambda<Func<TSource, bool>>(binaryExpression, pe).Compile();//构建查询表达式:第一个参数为匹配表达式binaryExpression,第2个参数为对象 //  p=>p.Name==Value表达式构建成功return res;
}
//最后在调用时:persons.Where(compareExp).ToList();表现为:persons.Where(p=>p.Name==value).ToList();的形式

5.Linq提供程序

LINQ 提供程序为特定的数据源实现了标准的查询操作符。

LINQ 提供程序也许会实现LINQ 定义的更多扩展方法,但至少要实现标准操作符。

LINQ to XML 不仅实现了专门用于XML 的方法,还实现了其他方法,例如System.Xml.Linq 命名空间的Extensions 类定义的方法Elements()、 
Descendants 和Ancestors。 
LINQ 提供程序的实现方案是根据命名空间第一个参数的类型来选择的。

实现扩展方法的类的命名空间必须是打开的,否则扩展类就不在作用域内。

在LINQ to Objects 中定义的Wherer()方法参数和在LINQ to SQL中定义的Wherer()方法参数是不同的。 
LINQ to Objects 中的Wherer()方法是用Enumerable 类定义的: 
public static IEnumerable <TSource> Where <TSource> (this IEnumerable <TSource> source, Func <TSource, bool> predicate); 
在 System.Linq 命名空间中,还有另一个类实现了操作符Where。这个实现代码由LINQ to SQL 使用, 
这些代码在类Queryable 中: 
public static IQueryable <TSource> Where <TSource> (this IQueryable <TSource> source, Expression < Func <TSource, bool> > predicate); 
这两个类都在System.Linq 命名空间的System.Core 程序集中实现。

那么,它是如何定义的?使用了什么方法?

无论是用Func<TSource, bool>参数传送,还是用Expression< Func<TSource,bool>>参数传送,

λ表达式都是相同的。只是编译器的操作是不同的,它根据source 参数来选择。

编译器根据其参数选择最匹配的方法。

在LINQ to SQL 中定义的DataContext 类的GetTable()方法返回IQueryable<TSource>,因此LINQ to SQL 使用类Queryable 的Wherer()方法。 
LINQ to SQL 提供程序使用表达式树,实现了接口IQueryable 和IQueryProvider。

转载于:https://www.cnblogs.com/licin/p/6899389.html

C#高级编程9 第11章 Linq相关推荐

  1. C#高级编程9 第17章 使用VS2013-C#特性

    C#高级编程9 第17章 使用VS2013 编辑定位到 如果默认勾选了这项,请去掉勾选,因为勾选之后解决方案的目录会根据当前文件选中. 可以设置项目并行生成数 版本控制软件设置 所有文本编辑器行号显示 ...

  2. C#高级编程9 第18章 部署

    C#高级编程9 第18章 部署 使用 XCopy 进行部署 本主题演示如何通过将应用程序文件从一台计算机复制到另一台计算机来部署应用程序. 1.将项目中生成的程序集复制到目标计算机,生成的程序集位于项 ...

  3. UINX环境高级编程笔记 第3章 文件I/O

    UNIX环境高级编程 第三章 文件I/O 3.1 引言 3.2 文件描述符 3.3 函数open和openat 3.4 函数creat 3.5 函数close 3.6 函数lseek 3.7 函数re ...

  4. 弟子规python编程游戏_《Python游戏趣味编程》 第11章 消灭星星

    知乎视频​www.zhihu.com 图书简介可以看这里: 童晶:<Python游戏趣味编程>新书上架了​zhuanlan.zhihu.com 消灭星星是一款非常容易上瘾的消除类游戏,只需 ...

  5. 《Python游戏趣味编程》 第11章 消灭星星

    11 消灭星星 图书简介可以看这里: 童晶:<Python游戏趣味编程>新书上架了 消灭星星是一款非常容易上瘾的消除类游戏,只需点击一个方块,如果和其连接的有两个或两个以上颜色相同的方块即 ...

  6. 《Python核心编程》第11章 函数和函数式编程 练习

    11-3 函数. 在这个练习中,我们将实现max()和min()内建函数. (a) 写分别带两个元素返回一个较大和较小元素,简单的max2()核min2()函数.他们应该可以用任意的python 对象 ...

  7. 第四篇 群聚类非线性表的编程实验 第11章 应用图的遍历算法编程

    11.1 BFS算法的实验范例 §6.1 概述           图         图结构是描述和解决实际应用问题的一种基本而有力的工具.所谓的图(graph),可定义为G = (V, E).其中 ...

  8. MySQL高级篇_第11章_数据库的设计规范

    1. 为什么需要数据库设计 我们在设计数据表的时候,要考虑很多问题. 比如 用户都需要什么数据?需要在数据表中保存哪些数据 如何保证数据表中数据的 正确性,当插入.删除.更新的时候该进行怎样的 约束检 ...

  9. Java笔记21-Java高级编程部分-第十三章-IO流

    第13章:IO流 目录: 13.1.File类的使用 FileTest package com.pfl.java3;import org.junit.Test;import java.io.File; ...

最新文章

  1. linux 内存居高不下,Linux 内存占用总是很高为何依旧很流畅?
  2. SQL Server 2012 安全新特性:包含数据库
  3. Java实现K-means
  4. IOS15的抽屉效果
  5. 递归函数(九):最小不动点定理
  6. 阿里云产品头条(2018年1月刊)
  7. 在python中输入圆的半_python根据圆的参数方程求圆上任意一点的坐标
  8. linux职业_对Linux的好奇心导致了意外的职业
  9. php mongodb _id,mongodb php findone()由ID
  10. 华三 h3c PBR策略配置
  11. Android Excel 解析 xls 和 xlsx,方法也可以很简单
  12. TC与CATIA集成使用说明
  13. idea构建post请求_IDEA-HTTP工具
  14. 二级vb计算机考试试题,全国计算机vb二级考试真题
  15. 计算机仿真系统模型有,计算机仿真在光伏发电系统模型中的应用研究原稿(最终定稿)...
  16. exls表格搜索快捷键_excel表格快捷键大全_如何在EXCEL表格中快速查找
  17. 计算机科学导论实验报告,上海电力学院计算机导论实验报告.docx
  18. mysql cluster 分片_MySQL Cluster --01
  19. Reflector 引起VS2013无法调试(Debug)并异常关闭
  20. 深聊性能测试,从入门到放弃之:如何对IO进行性能调优

热门文章

  1. 离线状态下配置深度学习服务器-在ubuntu16.04 上安装python,pip以及包
  2. 重磅!继“智能+”120页PPT,阿里+毕马威发布4份智能经济报告(附免费下载)
  3. 10、zuul 网关配置
  4. 通过nvm管理node版本
  5. NC65点击单据按钮打开web窗口
  6. CSR8675学习笔记:新建一个GATT server
  7. STM32MP157驱动开发——Linux自带的LED灯驱动
  8. ArcGIS Pro基于遥感影像使用深度学习地物识别评估植被健康情况(教程)
  9. Android 小米手环蓝牙
  10. Python statsmodel回归 添加截距项