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>();
racers.Add(new Racer("Nino", "Farina", "Italy", , , new int[] { }, new string[] { "Alfa Romeo" }));
racers.Add(new Racer("Alberto", "Ascari", "Italy", , , new int[] { , }, new string[] { "Ferrari" }));
racers.Add(new Racer("Juan Manuel", "Fangio", "Argentina", , , new int[] { , , , , }, new string[] { "Alfa Romeo", "Maserati", "Mercedes", "Ferrari" }));
racers.Add(new Racer("Mike", "Hawthorn", "UK", , , new int[] { }, new string[] { "Ferrari" }));
racers.Add(new Racer("Phil", "Hill", "USA", , , new int[] { }, new string[] { "Ferrari" }));
racers.Add(new Racer("John", "Surtees", "UK", , , new int[] { }, new string[] { "Ferrari" }));
racers.Add(new Racer("Jim", "Clark", "UK", , , new int[] { , }, new string[] { "Lotus" }));
racers.Add(new Racer("Jack", "Brabham", "Australia", , , new int[] { , , }, new string[] { "Cooper", "Brabham" }));
racers.Add(new Racer("Denny", "Hulme", "New Zealand", , , new int[] { }, new string[] { "Brabham" }));
racers.Add(new Racer("Graham", "Hill", "UK", , , new int[] { , }, new string[] { "BRM", "Lotus" }));
racers.Add(new Racer("Jochen", "Rindt", "Austria", , , new int[] { }, new string[] { "Lotus" }));
racers.Add(new Racer("Jackie", "Stewart", "UK", , , new int[] { , , }, new string[] { "Matra", "Tyrrell" }));
racers.Add(new Racer("Emerson", "Fittipaldi", "Brazil", , , new int[] { , }, new string[] { "Lotus", "McLaren" }));
racers.Add(new Racer("James", "Hunt", "UK", , , new int[] { }, new string[] { "McLaren" }));
racers.Add(new Racer("Mario", "Andretti", "USA", , , new int[] { }, new string[] { "Lotus" }));
racers.Add(new Racer("Jody", "Scheckter", "South Africa", , , new int[] { }, new string[] { "Ferrari" }));
racers.Add(new Racer("Alan", "Jones", "Australia", , , new int[] { }, new string[] { "Williams" }));
racers.Add(new Racer("Keke", "Rosberg", "Finland", , , new int[] { }, new string[] { "Williams" }));
racers.Add(new Racer("Niki", "Lauda", "Austria", , , new int[] { , , }, new string[] { "Ferrari", "McLaren" }));
racers.Add(new Racer("Nelson", "Piquet", "Brazil", , , new int[] { , , }, new string[] { "Brabham", "Williams" }));
racers.Add(new Racer("Ayrton", "Senna", "Brazil", , , new int[] { , , }, new string[] { "McLaren" }));
racers.Add(new Racer("Nigel", "Mansell", "UK", , , new int[] { }, new string[] { "Williams" }));
racers.Add(new Racer("Alain", "Prost", "France", , , new int[] { , , , }, new string[] { "McLaren", "Williams" }));
racers.Add(new Racer("Damon", "Hill", "UK", , , new int[] { }, new string[] { "Williams" }));
racers.Add(new Racer("Jacques", "Villeneuve", "Canada", , , new int[] { }, new string[] { "Williams" }));
racers.Add(new Racer("Mika", "Hakkinen", "Finland", , , new int[] { , }, new string[] { "McLaren" }));
racers.Add(new Racer("Michael", "Schumacher", "Germany", , , new int[] { , , , , , , }, new string[] { "Benetton", "Ferrari" }));
racers.Add(new Racer("Fernando", "Alonso", "Spain", , , new int[] { , }, new string[] { "Renault" }));
racers.Add(new Racer("Kimi", "Räikkönen", "Finland", , , new int[] { }, new string[] { "Ferrari" }));
racers.Add(new Racer("Lewis", "Hamilton", "UK", , , new int[] { }, new string[] { "McLaren" }));
racers.Add(new Racer("Jenson", "Button", "UK", , , new int[] { }, new string[] { "Brawn GP" }));
racers.Add(new Racer("Sebastian", "Vettel", "Germany", , , new int[] { , }, 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", ),
new Team("Cooper", , ),
new Team("Ferrari", , , , , , , , , , , , , , , , ),
new Team("BRM", ),
new Team("Lotus", , , , , , , ),
new Team("Brabham", , ),
new Team("Matra", ),
new Team("Tyrrell", ),
new Team("McLaren", , , , , , , , ),
new Team("Williams", , , , , , , , , ),
new Team("Benetton", ),
new Team("Renault", , ),
new Team("Brawn GP", ),
new Team("Red Bull Racing", , )
};
}
return teams;
} private static List<Championship> championships;
public static IEnumerable<Championship> GetChampionships()
{
if (championships == null)
{
championships = new List<Championship>();
championships.Add(new Championship
{
Year = ,
First = "Nino Farina",
Second = "Juan Manuel Fangio",
Third = "Luigi Fagioli"
});
championships.Add(new Championship
{
Year = ,
First = "Juan Manuel Fangio",
Second = "Alberto Ascari",
Third = "Froilan Gonzalez"
});
championships.Add(new Championship
{
Year = ,
First = "Alberto Ascari",
Second = "Nino Farina",
Third = "Piero Taruffi"
});
championships.Add(new Championship
{
Year = ,
First = "Alberto Ascari",
Second = "Juan Manuel Fangio",
Third = "Nino Farina"
});
championships.Add(new Championship
{
Year = ,
First = "Juan Manuel Fangio",
Second = "Froilan Gonzalez",
Third = "Mike Hawthorn"
});
championships.Add(new Championship
{
Year = ,
First = "Juan Manuel Fangio",
Second = "Stirling Moss",
Third = "Eugenio Castellotti"
});
championships.Add(new Championship
{
Year = ,
First = "Juan Manuel Fangio",
Second = "Stirling Moss",
Third = "Peter Collins"
});
championships.Add(new Championship
{
Year = ,
First = "Juan Manuel Fangio",
Second = "Stirling Moss",
Third = "Luigi Musso"
});
championships.Add(new Championship
{
Year = ,
First = "Mike Hawthorn",
Second = "Stirling Moss",
Third = "Tony Brooks"
});
championships.Add(new Championship
{
Year = ,
First = "Jack Brabham",
Second = "Tony Brooks",
Third = "Stirling Moss"
});
championships.Add(new Championship
{
Year = ,
First = "Jack Brabham",
Second = "Bruce McLaren",
Third = "Stirling Moss"
});
championships.Add(new Championship
{
Year = ,
First = "Phil Hill",
Second = "Wolfgang von Trips",
Third = "Stirling Moss"
});
championships.Add(new Championship
{
Year = ,
First = "Graham Hill",
Second = "Jim Clark",
Third = "Bruce McLaren"
});
championships.Add(new Championship
{
Year = ,
First = "Jim Clark",
Second = "Graham Hill",
Third = "Richie Ginther"
});
championships.Add(new Championship
{
Year = ,
First = "John Surtees",
Second = "Graham Hill",
Third = "Jim Clark"
});
championships.Add(new Championship
{
Year = ,
First = "Jim Clark",
Second = "Graham Hill",
Third = "Jackie Stewart"
});
championships.Add(new Championship
{
Year = ,
First = "Jack Brabham",
Second = "John Surtees",
Third = "Jochen Rindt"
});
championships.Add(new Championship
{
Year = ,
First = "Dennis Hulme",
Second = "Jack Brabham",
Third = "Jim Clark"
});
championships.Add(new Championship
{
Year = ,
First = "Graham Hill",
Second = "Jackie Stewart",
Third = "Dennis Hulme"
});
championships.Add(new Championship
{
Year = ,
First = "Jackie Stewart",
Second = "Jackie Ickx",
Third = "Bruce McLaren"
});
championships.Add(new Championship
{
Year = ,
First = "Jochen Rindt",
Second = "Jackie Ickx",
Third = "Clay Regazzoni"
});
championships.Add(new Championship
{
Year = ,
First = "Jackie Stewart",
Second = "Ronnie Peterson",
Third = "Francois Cevert"
});
championships.Add(new Championship
{
Year = ,
First = "Emerson Fittipaldi",
Second = "Jackie Stewart",
Third = "Dennis Hulme"
});
championships.Add(new Championship
{
Year = ,
First = "Jackie Stewart",
Second = "Emerson Fittipaldi",
Third = "Ronnie Peterson"
});
championships.Add(new Championship
{
Year = ,
First = "Emerson Fittipaldi",
Second = "Clay Regazzoni",
Third = "Jody Scheckter"
});
championships.Add(new Championship
{
Year = ,
First = "Niki Lauda",
Second = "Emerson Fittipaldi",
Third = "Carlos Reutemann"
});
championships.Add(new Championship
{
Year = ,
First = "James Hunt",
Second = "Niki Lauda",
Third = "Jody Scheckter"
});
championships.Add(new Championship
{
Year = ,
First = "Niki Lauda",
Second = "Jody Scheckter",
Third = "Mario Andretti"
});
championships.Add(new Championship
{
Year = ,
First = "Mario Andretti",
Second = "Ronnie Peterson",
Third = "Carlos Reutemann"
});
championships.Add(new Championship
{
Year = ,
First = "Jody Scheckter",
Second = "Gilles Villeneuve",
Third = "Alan Jones"
});
championships.Add(new Championship
{
Year = ,
First = "Alan Jones",
Second = "Nelson Piquet",
Third = "Carlos Reutemann"
});
championships.Add(new Championship
{
Year = ,
First = "Nelson Piquet",
Second = "Carlos Reutemann",
Third = "Alan Jones"
});
championships.Add(new Championship
{
Year = ,
First = "Keke Rosberg",
Second = "Didier Pironi",
Third = "John Watson"
});
championships.Add(new Championship
{
Year = ,
First = "Nelson Piquet",
Second = "Alain Prost",
Third = "Rene Arnoux"
});
championships.Add(new Championship
{
Year = ,
First = "Niki Lauda",
Second = "Alain Prost",
Third = "Elio de Angelis"
});
championships.Add(new Championship
{
Year = ,
First = "Alain Prost",
Second = "Michele Alboreto",
Third = "Keke Rosberg"
});
championships.Add(new Championship
{
Year = ,
First = "Alain Prost",
Second = "Nigel Mansell",
Third = "Nelson Piquet"
});
championships.Add(new Championship
{
Year = ,
First = "Nelson Piquet",
Second = "Nigel Mansell",
Third = "Ayrton Senna"
});
championships.Add(new Championship
{
Year = ,
First = "Ayrton Senna",
Second = "Alain Prost",
Third = "Gerhard Berger"
});
championships.Add(new Championship
{
Year = ,
First = "Alain Prost",
Second = "Ayrton Senna",
Third = "Riccardo Patrese"
});
championships.Add(new Championship
{
Year = ,
First = "Ayrton Senna",
Second = "Alain Prost",
Third = "Nelson Piquet"
});
championships.Add(new Championship
{
Year = ,
First = "Ayrton Senna",
Second = "Nigel Mansell",
Third = "Riccardo Patrese"
});
championships.Add(new Championship
{
Year = ,
First = "Nigel Mansell",
Second = "Riccardo Patrese",
Third = "Michael Schumacher"
});
championships.Add(new Championship
{
Year = ,
First = "Alain Prost",
Second = "Ayrton Senna",
Third = "Damon Hill"
});
championships.Add(new Championship
{
Year = ,
First = "Michael Schumacher",
Second = "Damon Hill",
Third = "Gerhard Berger"
});
championships.Add(new Championship
{
Year = ,
First = "Michael Schumacher",
Second = "Damon Hill",
Third = "David Coulthard"
});
championships.Add(new Championship
{
Year = ,
First = "Damon Hill",
Second = "Jacques Villeneuve",
Third = "Michael Schumacher"
});
championships.Add(new Championship
{
Year = ,
First = "Jacques Villeneuve",
Second = "Heinz-Harald Frentzen",
Third = "David Coulthard"
});
championships.Add(new Championship
{
Year = ,
First = "Mika Hakkinen",
Second = "Michael Schumacher",
Third = "David Coulthard"
});
championships.Add(new Championship
{
Year = ,
First = "Mika Hakkinen",
Second = "Eddie Irvine",
Third = "Heinz-Harald Frentzen"
});
championships.Add(new Championship
{
Year = ,
First = "Michael Schumacher",
Second = "Mika Hakkinen",
Third = "David Coulthard"
});
championships.Add(new Championship
{
Year = ,
First = "Michael Schumacher",
Second = "David Coulthard",
Third = "Rubens Barrichello"
});
championships.Add(new Championship
{
Year = ,
First = "Michael Schumacher",
Second = "Rubens Barrichello",
Third = "Juan Pablo Montoya"
});
championships.Add(new Championship
{
Year = ,
First = "Michael Schumacher",
Second = "Kimi Räikkönen",
Third = "Juan Pablo Montoya"
});
championships.Add(new Championship
{
Year = ,
First = "Michael Schumacher",
Second = "Rubens Barrichello",
Third = "Jenson Button"
});
championships.Add(new Championship
{
Year = ,
First = "Fernando Alonso",
Second = "Kimi Räikkönen",
Third = "Michael Schumacher"
});
championships.Add(new Championship
{
Year = ,
First = "Fernando Alonso",
Second = "Michael Schumacher",
Third = "Felipe Massa"
});
championships.Add(new Championship
{
Year = ,
First = "Kimi Räikkönen",
Second = "Lewis Hamilton",
Third = "Fernando Alonso"
});
championships.Add(new Championship
{
Year = ,
First = "Lewis Hamilton",
Second = "Felipe Massa",
Third = "Kimi Raikkonen"
});
championships.Add(new Championship
{
Year = ,
First = "Jenson Button",
Second = "Sebastian Vettel",
Third = "Rubens Barrichello"
});
championships.Add(new Championship
{
Year = ,
First = "Sebastian Vettel",
Second = "Fernando Alonso",
Third = "Mark Webber"
});
championships.Add(new Championship
{
Year = ,
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: , wins: ));
moreRacers.Add(new Racer("Jose Froilan", "Gonzalez", "Argentina", starts: , wins: ));
moreRacers.Add(new Racer("Piero", "Taruffi", "Italy", starts: , wins: ));
moreRacers.Add(new Racer("Stirling", "Moss", "UK", starts: , wins: ));
moreRacers.Add(new Racer("Eugenio", "Castellotti", "Italy", starts: , wins: ));
moreRacers.Add(new Racer("Peter", "Collins", "UK", starts: , wins: ));
moreRacers.Add(new Racer("Luigi", "Musso", "Italy", starts: , wins: ));
moreRacers.Add(new Racer("Tony", "Brooks", "UK", starts: , wins: ));
moreRacers.Add(new Racer("Bruce", "McLaren", "New Zealand", starts: , wins: ));
moreRacers.Add(new Racer("Wolfgang von", "Trips", "Germany", starts: , wins: ));
moreRacers.Add(new Racer("Richie", "Ginther", "USA", starts: , wins: ));
moreRacers.Add(new Racer("Jackie", "Ickx", "Belgium", starts: , wins: ));
moreRacers.Add(new Racer("Clay", "Regazzoni", "Switzerland", starts: , wins: ));
moreRacers.Add(new Racer("Ronnie", "Peterson", "Sweden", starts: , wins: ));
moreRacers.Add(new Racer("Francois", "Cevert", "France", starts: , wins: ));
moreRacers.Add(new Racer("Carlos", "Reutemann", "Argentina", starts: , wins: ));
moreRacers.Add(new Racer("Gilles", "Villeneuve", "Canada", starts: , wins: ));
moreRacers.Add(new Racer("Didier", "Pironi", "France", starts: , wins: ));
moreRacers.Add(new Racer("John", "Watson", "UK", starts: , wins: ));
moreRacers.Add(new Racer("Rene", "Arnoux", "France", starts: , wins: ));
moreRacers.Add(new Racer("Elio", "de Angelis", "Italy", starts: , wins: ));
moreRacers.Add(new Racer("Michele", "Alboreto", "Italy", starts: , wins: ));
moreRacers.Add(new Racer("Gerhard", "Berger", "Austria", starts: , wins: ));
moreRacers.Add(new Racer("Riccardo", "Patrese", "Italy", starts: , wins: ));
moreRacers.Add(new Racer("David", "Coulthard", "UK", starts: , wins: ));
moreRacers.Add(new Racer("Heinz-Harald", "Frentzen", "Germany", starts: , wins: ));
moreRacers.Add(new Racer("Eddie", "Irvine", "UK", starts: , wins: ));
moreRacers.Add(new Racer("Rubens", "Barrichello", "Brazil", starts: , wins: ));
moreRacers.Add(new Racer("Juan Pablo", "Montoya", "Columbia", starts: , wins: ));
moreRacers.Add(new Racer("Felipe", "Massa", "Brazil", starts: , wins: ));
moreRacers.Add(new Racer("Mark", "Webber", "Australia", starts: , wins: ));
}
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 -;
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 descending
select r; foreach (var r in query)
{
Console.WriteLine("{0:A}", r);
}

查询结果:

Ayrton Senna, Brazil; starts: , wins:
Nelson Piquet, Brazil; starts: , wins:
Emerson Fittipaldi, Brazil; starts: , wins:

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 > && (r.Country == "Brazil" || r.Country == "Austria")
select r; foreach (var r in racers)
{
Console.WriteLine("{0:A}", r);
}

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

var racers = Formula1.GetChampions().Where(r => r.Wins >  && (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 % != );
foreach (var r in racers)
{
Console.WriteLine("{0:A}", r);
}
类型筛选

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

object[] data = { "one", , , "four", "five",  };
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.Cars
where c == "Ferrari"
orderby r.LastName
select r.FirstName + " " + r.LastName; foreach (var racer in ferrariDrivers)
{
Console.WriteLine(racer);
}

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 descending
select r;
var racers = (from r in Formula1.GetChampions()
orderby r.Country, r.LastName, r.FirstName
select r).Take(); 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();

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

分组

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

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

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

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

var countries2 = Formula1.GetChampions().
GroupBy(r => r.Country).
OrderByDescending(g => g.Count()).
ThenBy(g => g.Key).
Where(g => g.Count() >= ).
Select(g => new { Country = g.Key, Count = g.Count() });
对嵌套的对象分组

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

var countries = from r in Formula1.GetChampions()
group r by r.Country into g
orderby g.Count() descending, g.Key
where g.Count() >=
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();
}
内连接

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

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
};
var racersAndTeams =
(from r in racers
join t in teams on r.Year equals t.Year
orderby t.Year
select new
{
Year = r.Year,
Champion = r.Name,
Constructor = t.Name
}).Take(); 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.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
}; 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
}).Take(); 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);
}
组连接

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

var racers = Formula1.GetChampionships()
.SelectMany(cs => new List<RacerInfo>()
{
new RacerInfo {
Year = cs.Year,
Position = ,
FirstName = cs.First.FirstName(),
LastName = cs.First.LastName()
},
new RacerInfo {
Year = cs.Year,
Position = ,
FirstName = cs.Second.FirstName(),
LastName = cs.Second.LastName()
},
new RacerInfo {
Year = cs.Year,
Position = ,
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);
}
}
集合操作

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

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

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

var racers = Formula1.GetChampionships().SelectMany(cs => new List<RacerInfo>()
{
new RacerInfo {
Year = cs.Year,
Position = ,
FirstName = cs.First.FirstName(),
LastName = cs.First.LastName()
},
new RacerInfo {
Year = cs.Year,
Position = ,
FirstName = cs.Second.FirstName(),
LastName = cs.Second.LastName()
},
new RacerInfo {
Year = cs.Year,
Position = ,
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);
}
分区

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

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
};
聚合操作符

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

int pageSize = ;

      int numberPages = (int)Math.Ceiling(Formula1.GetChampions().Count() /
(double)pageSize); for (int page = ; 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();
}
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(); foreach (var country in countries)
{
Console.WriteLine("{0} {1}", country.Country, country.Wins);
}
转换操作符

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 descending
select r;
foreach (var racer in query)
{
Console.WriteLine("{0:A}", racer);
}
生成操作符

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

var values=Enumerable.Range(,);
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 = ;
var r = new Random();
return Enumerable.Range(, arraySize).Select(x => r.Next()).ToList();
} static void IntroParallel()
{
var data = SampleData(); var watch = new Stopwatch(); watch.Start();
var q1 = (from x in data
where Math.Log(x) <
select 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) <
select x).Average();
watch.Stop();
Console.WriteLine("async {0}, result: {1}", watch.ElapsedMilliseconds, q2);
}
分区器

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

var res = (from x in data.AsParallel().WithCancellation(cts.Token)
where Math.Log(x) <
select 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) <
select 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

接下来看看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 = },
new Person(){ Name = "daisy", age = },
new Person(){Name="dom", age=},
new Person(){Name="caren", age=}};
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);//构建表达式的对象属性,属性名为value
var 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。

最新文章

  1. iOS 委托与文本输入(内容根据iOS编程编写)
  2. 机器学习——支持向量机(SVM)之Platt SMO算法
  3. linux 网络编程比较好的文章
  4. java 框架Nutz
  5. Android Please ensure that adb is correctly located at问题解决
  6. 智能指针(三):unique_ptr使用简介
  7. 设计模式知识搜集(c++)
  8. 【Timers】win服务 定时器
  9. POJ 3671 Dining Cows (DP,LIS, 暴力)
  10. call_user_func
  11. Hibernate试题解析
  12. nagios 安装和配置(含有nrpe结束)所有 (两)
  13. AJAX应用【股票案例】
  14. 翻译:CREATE FUNCTION语句(已提交到MariaDB官方手册)
  15. 理解和使用ThreadLocal类
  16. EF延迟加载和懒加载
  17. Flume 拦截器(interceptor)详解
  18. Redis哨兵(sentinel)模式搭建
  19. 搜索关键字自动更正 - Oracle Endeca Server
  20. 【转】Win10/UWP新特性系列—Web

热门文章

  1. InfluxDB系列之一安装及简单运维(未完成,需要继续写)
  2. 十七、springboot配置FastJson为Spring Boot默认JSON解析框架
  3. 02 How to Write Go Code 如何编写go语言代码
  4. mysqlbinlog的日志类型
  5. 监听 手机back键和顶部的回退
  6. java 闭包与回调
  7. SQL Server日期计算
  8. Ubuntu Touch On Nexus4 Manual Install (手动安装) under Gentoo
  9. post提交数据的四种编码方式
  10. 好久没有写过SQL了,今天写了一句select in留存