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 \t 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()方法返回一個迭代器,該迭代器把同一個值重復特定的次數。