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。

C#高级编程9 第11章 Linq的更多相关文章

  1. 读《C#高级编程》第1章问题

    读<C#高级编程>第1章 .Net机构体系笔记 网红的话:爸爸说我将来会是一个牛逼的程序员,因为我有一个梦,虽然脑壳笨但是做事情很能坚持. 本章主要是了解.Net的结构,都是一些概念,并没 ...

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

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

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

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

  4. C#高级编程9 第16章 错误和异常

    C#高级编程9 第16章 错误和异常 了解这章可以学会如何处理系统异常以及错误信息. System.Exception类是.NET运行库抛出的异常,可以继承它定义自己的异常类. try块代码包含的代码 ...

  5. C#高级编程笔记之第二章:核心C#

    变量的初始化和作用域 C#的预定义数据类型 流控制 枚举 名称空间 预处理命令 C#编程的推荐规则和约定 变量的初始化和作用域 初始化 C#有两个方法可以一确保变量在使用前进行了初始化: 变量是字段, ...

  6. Windows核心编程:第11章 Windows线程池

    Github https://github.com/gongluck/Windows-Core-Program.git //第11章 Windows线程池.cpp: 定义应用程序的入口点. // #i ...

  7. C#高级编程9 第14章 内存管理和指针

    C#高级编程9 内存管理和指针 后台内存管理 1) 值数据类型 在处理器的虚拟内存中有一个区域,称为栈,栈存储变量的浅副本数据,通过进入变量的作用域划分区域,通过离开变量的作用域释放. 栈的指针指向栈 ...

  8. C#高级编程(第9版) 第11章 LINQ 笔记

    概述语言集成查询(Language Integrated Query, LINQ)在C#编程语言中集成了查询语法,可以用相同的语法访问不同的数据源.LINQ提供了不同数据源的抽象层,所以可以使用相同的 ...

  9. python高级编程(第12章:优化学习)1

    # -*- coding: utf-8 -*-# python:2.x__author__ = 'Administrator'#由于5,6,7,8,9,10,11主要是在包,测试之类的学习所以这边就不 ...

随机推荐

  1. aarch64_g4

    golang-github-inconshreveable-muxado-devel-0-0.7.gitf693c7e.fc26.noarch.rpm 2017-02-11 16:47 30K fed ...

  2. sicily 1231. The Embarrassed Cryptography

    Time Limit: 2sec    Memory Limit:32MB  Description The young and very promising cryptographer Odd Ev ...

  3. 关于bcb调用动态库,contains invalid OMF record, type 0x21 (possibly COFF)问题

    今天用C++Builder6.0 调用三方lib文件时,编译的时候出现如下错误: “contains invalid OMF record, type 0x21 (possibly COFF)” 才知 ...

  4. 02 Go 1.2 Release Notes

    Go 1.2 Release Notes Introduction to Go 1.2 Changes to the language Use of nil Three-index slices Ch ...

  5. linux java配置

    1.java配置 配置环境变量在/etc/profile下增加# set Java environmentJAVA_HOME=/usr/share/jdk1.6.0_43PATH=$JAVA_HOME ...

  6. js字符串基本操作

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN""http://www.w3.org/TR/xhtm ...

  7. Selenium_多线程执行测试用例

    多线程执行测试用例 这里以百度搜索为例,通过不同的浏览器来启动不同的线程. #!/usr/bin/env python # _*_ coding:utf-8 _*_ __author__ = 'Yin ...

  8. C++两个类相互包含引用的问题

    在构造自己的类时,有可能会碰到两个类之间的相互引用问题,例如:定义了类A类B,A中使用了B定义的类型,B中也使用了A定义的类型 class A { B b; } class B { A* a; } 请 ...

  9. 树莓派3B更换源为阿里源

    sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak #科大源 sudo nano /etc/apt/sources.list deb htt ...

  10. LANMPS 一键PHP环境安装包(转)

    转:http://www.lanmps.com/ LANMPS 一键安装包,php绿色环境套件包: https://github.com/foxiswho/lanmps 安装 Mar 17, 2017 ...