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方法。本文后边将会用到。