1:筛选
Where :使用where子句,可以合并多个表达式。例如:找出赢得至少15场比赛的美国和英国赛车手
var racers = from r in Formulal.GetChampions()
where r.Wins > 15 && (r.Country == "USA" || r.Country == "UK")
select r; foreach (var r in racers) { Console.WriteLine($"{r:A}"); }
一样的结果:
var racers2 = Formulal.GetChampions().Where(r => r.Wins > 15 && (r.Country == "USA" || r.Country == "UK")).Select(r=>r);
其中r:A中的A所指定
使用格式说明符可以创建格式字符串。 格式字符串的格式如下:Axx
,其中
A
是格式说明符,控制应用于数值的格式设置类型。xx
是精度说明符,影响格式化输出中的位数。 精度说明符值的范围为 0 到 99。
2:用索引筛选
在where()方法重载中,可以传递第二个参数--索引。
索引时筛选器返回的每个结果的计数器。可以在表达式中使用这个索引。执行基于索引的计算。
1 //查询姓氏以A开头的所有赛车手,并且索引是奇数的赛车手 2 var racers3 = Formulal.GetChampions()
.Where((r, index) => r.LastName.StartsWith("A") && index % 2 != 0); 3 foreach (var racer in racers3) 4 { 5 Console.WriteLine($"{racer:A}"); 6 }
3:类型筛选
为了基于类型筛选,可以使用OfType()扩展方法。
1 object[] data = {"one", 1, 3, "four", "five", 6}; 2 var query = data.OfType<string>(); 3 Console.WriteLine("输出string类型"); 4 foreach (var s in query) 5 { 6 Console.WriteLine(s); 7 } 8 9 Console.WriteLine("输出int类型"); 10 var query2 = data.OfType<int>(); 11 foreach (var q in query2) 12 { 13 Console.WriteLine(q); 14 }
4:复合的from子句
如果需要根据对象的一个成员进行筛选,而该成员本身是一个系列,就可以使用复合的from子句
下列例子中Cars是 r中的一个属性,Cars是一个字符串数组
//如果需要根据对象的一个成员进行筛选,而该成员本身是一个系列,就可以使用复合的from子句 var ferrariDrivers = from r in Formulal.GetChampions() from c in r.Cars where c == "Ferrari" orderby r.LastName select r.FirstName + " " + r.LastName; foreach (var item in ferrariDrivers) { Console.WriteLine(item); }
c#编译器把复合的from子句和Linq查询转换为SelectMany()扩展方法。
SelectMany()方法可用于迭代序列的序列。
1 Console.WriteLine("=====SelectMany查询方法======"); 2 var ferrariDrivers1 = Formulal.GetChampions().SelectMany(r => r.Cars, (r, c) => new {Racer = r, Car = c}).Where(r=>r.Car=="Ferrari").OrderBy(r=>r.Racer.LastName).Select(r=>r.Racer.FirstName+" "+r.Racer.LastName+" " +r.Car); 3 foreach (var item in ferrariDrivers1) 4 { 5 Console.WriteLine(item); 6 }
这个方法和from复合子句返回的结果是一样的。
5:排序
Orderby子句 和 orderby descending子句
1 var racers = from r in Formulal.GetChampions() 2 where r.Country == "Italy" 3 orderby r.Wins descending 4 select r; 5 foreach (var racer in racers) 6 { 7 Console.WriteLine(racer); 8 } 9 10 Console.WriteLine(); 11 var racers1 = Formulal.GetChampions().Where(r => r.Country == "Italy").OrderByDescending(r => r.Wins); 12 foreach (var racer in racers1) 13 { 14 Console.WriteLine(racer); 15 }
说明:OrderBy()和OrderByDescending()方法返回IOrderEnumerable<TSource>.这个接口派生自
IEnumerable<TSource>接口。但包含一个额外的方法CreateOrderedEnumerable<TSource>()。这个方法用于进一步给序列排序。如果根据关键字选择器排序,其中有两项相同,就可以使用ThenBy()和ThenByDescending()方法继续排序。可以添加任意多个ThenBy()和ThenByDesceding()方法对集合排序。
1 var racers2 = 2 (from r in Formulal.GetChampions() orderby r.Country, r.LastName, r.FirstName select r).Take(10); 3 foreach (var racer in racers2) 4 { 5 Console.WriteLine(racer); 6 } 7 8 Console.WriteLine(); 9 10 var racer3 = Formulal.GetChampions().OrderBy(r => r.Country).ThenBy(r => r.LastName) 11 .ThenBy(r => r.FirstName).Take(10); 12 foreach (var racer in racer3) 13 { 14 Console.WriteLine(racer); 15 }
6:分组
要根据一个关键字值对查询结果分组,可以使用group子句
//要根据一个关键字值对查询结果分组,可以使用group子句
//一级方程式冠军按照国家分组,并列出一个国家的冠军数。
//根据获得的冠军数降序排列 如果冠军数一样就按照关键字Key来排序 这里的Key=Country
1 var countries = from r in Formulal.GetChampions() 2 group r by r.Country into g 3 orderby g.Count() descending, g.Key 4 where g.Count() >= 2 5 select new 6 { 7 Country = g.Key, 8 Count = g.Count() 9 10 }; 11 12 foreach (var item in countries) 13 { 14 Console.WriteLine(value: $"{item.Country,-10} {item.Count}"); 15 } 16 17 var countries1 = Formulal.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() }); 18 foreach (var c in countries1) 19 { 20 Console.WriteLine($"{c.Country,-10}{c.Count}"); 21 }
7:Linq中的变量
////在分组编写的Linq查询中,Count方法调用了多次,使用let子句可以改变这种方式,let允许在linq查询中定义变量
1 var countries4 = from r in Formulal.GetChampions() 2 group r by r.Country into g 3 let count = g.Count() 4 orderby count descending, g.Key 5 where count >= 2 6 select new 7 { 8 Country = g.Key, 9 Count = count 10 }; 11 foreach (var item in countries4) 12 { 13 Console.WriteLine($"{item.Country,-10}{item.Count}"); 14 } 15 16 Console.WriteLine("============="); 17 //下面这个第一个Select 是用来创建匿名类型,这里创建了一个Group和Count属性的匿名类型 18 //带有这些属性的一组传递给OrderByDescending方法,基于匿名属性的Count进行排列 19 //不过这个要有一个注意点 应考虑根据let或select方法创建的临时对象的数量,查询大列表时,创建的大量对象需要以后进行垃圾收集,这可能对性能产生巨大的影响。 20 var countries5 = Formulal.GetChampions().GroupBy(r => r.Country) 21 .Select(g => new { Group = g, Count = g.Count() }).OrderByDescending(c => c.Count) 22 .ThenBy(g => g.Group.Key).Where(g => g.Count >= 2) 23 .Select(g => new { Country = g.Group.Key, Count = g.Count }); 24 25 foreach (var item in countries5) 26 { 27 Console.WriteLine($"{item.Country,-10}{item.Count}"); 28 }
8:对嵌套的对象分组
//如果分组的对象包含嵌套的序列,就可以改变select子句创建的匿名类型
var countries = from r in Formulal.GetChampions() group r by r.Country into g let count = g.Count() orderby count descending, g.Key where count >= 2 select new { Country = g.Key, Count = count, Racers = from r1 in g orderby r1.LastName select r1.FirstName + " " + r1.LastName }; foreach (var item in countries) { Console.WriteLine($"{item.Country,-10} {item.Count}"); Console.WriteLine("====车手姓名===="); foreach (var name in item.Racers) { Console.WriteLine($"{name}"); } }
9:内连接
1 //使用 join子句可以根据特定的条件合并两个数据源。 2 var racers = from r in Formulal.GetChampions() 3 from y in r.Years 4 select new 5 { 6 Year = y, 7 Name = r.FirstName + " " + r.LastName 8 }; 9 10 var teams = from r in Formulal.GetContructorChampions() 11 from y in r.Years 12 select new 13 { 14 Year = y, 15 Name = r.Name 16 }; 17 //通过join子句,根据赛车手获得的冠军的年份和车队活的冠军的年份进行连接。 18 var racersAndTeams = 19 (from r in racers 20 join t in teams on r.Year equals t.Year 21 select new {r.Year, champion = r.Name, Constructor = t.Name}).Take(10); 22 Console.WriteLine("Year World Champion Constructor Title"); 23 foreach (var item in racersAndTeams) 24 { 25 Console.WriteLine($"{item.Year}:{item.champion,-20} {item.Constructor}"); 26 }
10:左外连接
左外连接用join和DefaultIfEmpty构成左外连接
1 //左外连接返回左边序列中的全部元素,即使它们在右边的序列中并没有匹配的元素 2 //和sql 的左连接一样 3 var racers = from r in Formulal.GetChampions() 4 from y in r.Years 5 select new 6 { 7 Year = y, 8 Name = r.FirstName + " " + r.LastName 9 }; 10 11 var teams = from r in Formulal.GetContructorChampions() 12 from y in r.Years 13 select new 14 { 15 Year = y, 16 Name = r.Name 17 }; 18 //使用左外连接 用join 子句和DefalutIfEmpty方法定义 如果查询的左侧没有匹配的车队冠军,那么就使用DefaultIfEmpty方法定义其右侧的默认值 19 20 var racersAndTeams = (from r in racers 21 join t in teams on r.Year equals t.Year into rt 22 from t in rt.DefaultIfEmpty() 23 orderby r.Year 24 select new 25 { 26 Year=r.Year, 27 Champion=r.Name, 28 Constructor =t==null?"no constructor championship":t.Name 29 }).Take(10); 30 31 foreach (var item in racersAndTeams) 32 { 33 Console.WriteLine(item); 34 }
11:组连接
左外连接使用了组连接和into子句,它有一部分与组连接相同,只不过组链接不使用DefaultIfEmpty方法。
使用组连接时,可以连接两个独立的序列,对于其中一个序列中的某个元素,另一个序列中存在对应的一个项列表。
例子略:
12:集合操作
扩展方法Distinct(),Union(),Intersect()和Except()都是集合操作。
Distinct:去重
Union:生成两个序列的并集
Intersect :生成两个集的交集
Except:生成两个序列的差集
注意:集合操作通过调用实体类GetHashCode()和Equals()方法来比较对象,对于自定义比较,还可以传递一个实现了IEqualityComparer<T>接口的对象。
13:合并
Zip()方法允许一个谓词把两个相关的序列合并为一个
方法将第一个序列的每个元素与第二个序列中具有相同索引的元素合并在一起。第一个集合中的第一项与第二个集合中的第一项合并,第一个集合中的第二项会与第二个集合中的第二项合并。依此类推,如果两个序列的项数不同。Zip()方法就在达到最小集合的末尾时停止。
如果一个序列包含三个元素, 另一个序列具有四个元素, 则结果序列将只有三个元素。
var racerNames = from r in Formulal.GetChampions() where r.Country == "Italy" orderby r.Wins descending select new { Name = r.FirstName + " " + r.LastName }; var racerNamesAndStarts = from r in Formulal.GetChampions() where r.Country == "Italy" orderby r.Wins descending select new { LastName = r.LastName, Starts = r.Starts }; var racers5 = racerNames.Zip(racerNamesAndStarts, (first, second) => first.Name + ",Starts:" + second.Starts); foreach (var item in racers5) { Console.WriteLine(item); }
int[] numbers = { 1, 2, 3, 4 }; string[] words = { "one", "two", "three" }; var numbersAndWords = numbers.Zip(words, (first, second) => first + " " + second); foreach (var item in numbersAndWords) Console.WriteLine(item);
14:分区/分页
Take()和skip()可以用于分页。
Skip()方法先忽略根据页面大小和实际页数计算出的项数。就是跳过多少
Take()方法根据页面大小提取一定数量的项。取多少个。
1 int pageSize = 5; 2 int numberPages = (int) Math.Ceiling(Formulal.GetChampions().Count() / (double) pageSize); 3 for (int i = 0; i < numberPages; i++) 4 { 5 Console.WriteLine($"Page :{i}"); 6 var racers6 = (from r in Formulal.GetChampions() 7 orderby r.LastName, r.FirstName 8 select r.FirstName + " " + r.LastName).Skip(i * pageSize).Take(numberPages); 9 foreach (var item in racers6) 10 { 11 Console.WriteLine(item); 12 } 13 }
使用TakeWhile()和SkipWhile()扩展方法,可以根据传递的谓词,根据这个谓词的结果提取或跳过某些项
TakeWhile():如果指定的条件为 true,则返回序列中的元素,然后跳过剩余的元素。 这句话的意思就是当 条件成立时,就返回成立之前的项,后面的就不管了。
string[] fruits = { "apple", "banana", "mango", "orange", "passionfruit", "grape" }; IEnumerable<string> query = fruits.TakeWhile(fruit => String.Compare("orange", fruit, StringComparison.OrdinalIgnoreCase) != 0); foreach (string fruit in query) { Console.WriteLine(fruit); }
SkipWhile():只要满足指定的条件,就跳过序列中的元素,然后返回剩余元素。和TakeWhile()刚好相反。
1 string[] fruits = { "apple", "banana", "mango", "orange", 2 "passionfruit", "grape" }; 3 4 IEnumerable<string> query = 5 fruits.SkipWhile(fruit => String.Compare("orange", fruit, 6 StringComparison.OrdinalIgnoreCase) != 0); 7 8 foreach (string fruit in query) 9 { 10 Console.WriteLine(fruit); 11 }
15聚合操作符
聚合操作符(如Count、Sum、Min、Max、Average和Aggregate操作符)不返回一个序列。而返回一个值。
15.1 Count()扩展方法返回集合中的项数。
1 var query = from r in Formulal.GetChampions() 2 let numberYears = r.Years.Count() 3 where numberYears >= 3 4 orderby numberYears descending, r.LastName 5 select new 6 { 7 Name = r.FirstName + " " + r.LastName, 8 timesChampion = numberYears 9 }; 10 foreach (var r in query) 11 { 12 Console.WriteLine($"{r.Name} {r.timesChampion}"); 13 }
15.2 Sum()方法汇总序列中的所有数字
15.3 Min()方法返回集合中的最小值
15.4 Max()方法返回集合中的最大值
15.5 Average()方法计算集合中的平均值
15.6 Aggregate()方法可以传递一个lambda表达式,该表达式对所有的值进行聚合
16:转换操作符
查询可以推迟到访问数据项时再执行。在迭代中使用查询时,查询会执行。而使用转换操作符会立即执行查询,把查询结果放在数组、列表或字典中。
ToList()立即执行查询。
Dictionary<TKey,TValue>类只支持一个键对应一个值。Lookup<Tkey,TElement>类中,一个键可以对应多个值。
1 var racers7 = (from r in Formulal.GetChampions() 2 from c in r.Cars 3 select new 4 { 5 Car = c, Racer = r 6 }).ToLookup(cr => cr.Car, cr => cr.Racer); 7 if (racers7.Contains("Ferrari")) 8 { 9 foreach (var racer in racers7["Ferrari"]) 10 { 11 Console.WriteLine(racer); 12 } 13 }
如果需要在非类型化的集合上(如ArrayList)使用Linq查询,就可以使用Cast()方法。
1 var list = new ArrayList(Formulal.GetChampions() as ICollection ?? throw new InvalidOperationException()); 2 var query = from r in list.Cast<Racer>() where r.Country == "USA" select r; 3 foreach (var racer in query) 4 { 5 Console.WriteLine($"{racer:A}",racer); 6 }
17:生成操作符
生成操作符Range()、Empty()和Repeat()不是扩展方法,而是返回序列的正常静态方法。
在Linq to Objects中,这些方案可用于Enumerable类。
var values = Enumerable.Range(1, 20); foreach (var item in values) { Console.WriteLine($"{item}",item); }
Empty()方法返回一个不返回值的迭代器,它可以用于需要的一个集合的参数。其中可以给参数传递空集合。
Repeat()方法返回一个迭代器,该迭代器把同一个值重复特定的次数。