Linq是语言集成查询的简称。Linq简化了语言查询,是的程序员可以使用相同的语法查询不同的数据源。
文使用的Linq查询包含了两种方式,一种是直接查询,另一种是使用System.Linq中定义的扩展方法进行的查询。对于扩展方法查询的方式,使用了大量的Lambda表的是和系统定义的委托Func<T>。不熟悉这两个东西可以看相关的文章:
【Lambda表达式学习记录】【Action<T>和Func<T>委托】
一、准备供查询的数据
本文首先构建一个提供数据的实体类供以后的查询使用。本文使用的实体类包含了1950到2008年一级方程式锦标赛的冠军车队和冠军赛手。这些数据是使用了实体类和列表来准备的。
首先定义代表赛手的实体类Racer。Racer类定义了几个属性和一个重载的ToString()方法,该方法以指定的格式显示赛手。Racer类实现 了IFormattable接口,以支持格式字符串的不同变体。这个类还实现类IComparable<T>接口,用以根据赛手的 LastName进行排序。为了执行高级查询Racer类同时包含了单值属性如FirstName、LastName、Wins(获胜次数)、 Country(国籍)和Starts。同时也包含了多值属性如:Cars(赛手在获得冠军的年份所使用的赛车)和Years(赛手获得冠军的年份,赛手 可以多次获得冠军)。这个类具体的实现如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace MyLinqTest
- {
- [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 int Wins { get; set; }
- public string Country { 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 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 int CompareTo(Racer other)
- {
- if (other == null)
- {
- throw new ArgumentNullException("Other");
- }
- return this.LastName.CompareTo(other.LastName);
- }
- }
- }
第二个实体类是Team。这个类包含了车队冠军的名字和获得冠军的年份。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace MyLinqTest
- {
- [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; }
- }
- }
在方法声明中的 params 关键字之后不允许任何其他参数,并且在方法声明中只允许一个 params 关键字。
第三个类是Formula1.这个类定义方法GetChampions方法返回一组表示以及方程式赛车冠军的列表。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace MyLinqTest
- {
- public static class Formula1
- {
- private static List<Racer> racers;
- public static IList<Racer> GetChampions()
- {
- if (racers == null)
- {
- racers = new List<Racer>();
- 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> GetConstructorChampions()
- {
- 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;
- }
- }
- }
这个类还定义了一个方法:GetConstructorChampions()。用于返回所有车队冠军的列表。
二、System.Linq中的扩展方法
扩展方法的作用是将方法写入最初没有定义该方法的类中。还可以将方法添加到实现某个特定接口的任 何类中,这样多个类就可以使用相同的实现代码。对于扩展方法本文不做详细介绍,只是扩展方法在Linq查询中使用较多需要预先了解。尤其是 System.Core程序集下定义的System.Linq方法。本文后边将会用到。