[.net 面向對象編程基礎] (20) LINQ使用

[.net 面向對象編程基礎] (20)  LINQ使用html

經過上節LINQ的基礎知識的學習,咱們能夠開始使用LINQ來進行內存數據的查詢了,咱們上節說了LINQ的定義爲:Language Integrated Query(語言集成查詢)的簡稱,它是集成在.NET編程語言中的一種特性.算法

 1.LINQ的構架數據庫

 

 從這幅圖中,咱們能夠知道LINQ包括五個部分:LINQ to Objects、LINQ to XML、LINQ to SQL、LINQ to DataSet、LINQ to Entities。 編程

 

程序集c#

命名空間數組

描述框架

LINQ to Objects編程語言

System.Core.dll函數

System.Linq學習

提供對內存中集合操做的支持

LINQ to XML

System.Xml.Linq.dll

System.Xml.Linq

提供對XML數據源的操做的支持

LINQ to SQL

System.Data.Linq.dll

System.Data.Linq

提供對Sql Server數據源操做的支持。(微軟已宣佈再也不更新,推薦使用LINQ to Entities)

LINQ to DataSet

System.Data.DataSetExtensions.dll

System.Data

提供對離線數據操做的支持。

LINQ to Entities

System.Core.dll 和System.Data.Entity.dll

System.Linq 和System.Data.Objects

LINQ to Entities 是 Entity Framework 的一部分而且取代LINQ to SQL 做爲在數據庫上使用 LINQ 的標準機制。(Entity Framework 是由微軟發佈的開源對象-關係映射(ORM)框架,支持多種數據庫。)

 目前,還能夠下載其餘第三方提供程序,例如LINQ to JSON、LINQ to MySQL、LINQ to Amazon、LINQ to Flickr和LINQ to SharePoint。不管使用什麼數據源,均可以經過LINQ使用相同的API進行操做。

 1.LINQ操做語法 

LINQ查詢時有兩種語法可供選擇:查詢表達式(Query Expression)和方法語法(Fluent Syntax)

 .NET公共語言運行庫(CLR)並不具備查詢表達式的概念。因此,編譯器會在程序編譯時把查詢表達式轉換爲方法語法,即對擴展方法的調用。因此使用方法語法會讓咱們更加接近和了解LINQ的實現和本質,而且一些查詢只能表示爲方法調用。但另外一方面,查詢表達式一般會比較簡單和易讀。無論怎樣,這兩種語法是互相補充和兼容的,咱們能夠在一個查詢中混合使用查詢表達式和方法語法。 

 .net的設計者在類庫中定義了一系列的擴展方法來方便用戶操做集合對象,這些擴展方法構成了LINQ的查詢操做符

 如下擴展方法存在對應的查詢表達式關鍵字:Where、Select、SelectMany、OrderBy、ThenBy、OrderByDescending、ThenByDescending、GroupBy、Join、GroupJoin。

 LINQ查詢表達式 

約束

LINQ查詢表達式必須以from子句開頭,以select或group子句結束。

 

關鍵字

功能

from…in…

指定要查找的數據源以及範圍變量,多個from子句則表示從多個數據源查找數據。

注意:c#編譯器會把「複合from子句」的查詢表達式轉換爲SelectMany()擴展方法。

join…in…on…equals…

指定多個數據源的關聯方式

let

引入用於存儲查詢表達式中子表達式結果的範圍變量。一般能達到層次感會更好,使代碼更易於閱讀。

orderby、descending

指定元素的排序字段和排序方式。當有多個排序字段時,由字段順序肯定主次關係,可指定升序和降序兩種排序方式

where

指定元素的篩選條件。多個where子句則表示了並列條件,必須所有都知足才能入選。每一個where子句可使用謂詞&&、||鏈接多個條件表達式。

group

指定元素的分組字段。

select

指定查詢要返回的目標數據,能夠指定任何類型,甚至是匿名類型。(目前一般被指定爲匿名類型)

into

提供一個臨時的標識符。該標識能夠引用join、group和select子句的結果。

1)        直接出如今join子句以後的into關鍵字會被翻譯爲GroupJoin。(into以前的查詢變量能夠繼續使用)

2)        select或group子句以後的into它會從新開始一個查詢,讓咱們能夠繼續引入where, orderby和select子句,它是對分步構建查詢表達式的一種簡寫方式。(into以前的查詢變量都不可再使用)

 表達式的語法以下: 

 from [type] id in source      

[join [type] id in source on expr equals expr [into subGroup]]      

[from [type] id in source | let id = expr | where condition]      

[orderby ordering,ordering,ordering...]      

select expr | group expr by key      

[into id query]

<1>第一行: 

type是可選的,id是集合中的一項,source是一個集合,若是集合中的類型與type指定的類型不一樣則致使強制轉化

 <2>第二行:  

一個查詢表達式中能夠有0個或多個join子句,
這裏的source能夠不等於第一句中的source

 expr能夠是一個表達式 

[into subGroup] subGroup是一箇中間變量,它繼承自IGrouping,表明一個分組,也就是說「一對多」裏的「多」

 能夠經過這個變量獲得這一組包含的對象個數,以及這一組對象的鍵

<3>第三行:  

一個查詢表達式中能夠有1個或多個from子句 

一個查詢表達式中能夠有0個或多個let子句,let子句能夠建立一個臨時變量 

一個查詢表達式中能夠有0個或多個where子句,where子句能夠指定查詢條件 

<4>第四行: 

一個查詢表達式能夠有0個或多個排序方式 

每一個排序方式以逗號分割 

<5>第五行: 

一個查詢表達式必須以select或者group by結束 

select後跟要檢索的內容 

group by 是對檢索的內容進行分組 

<6>第六行: 

最後一個into子句起到的做用是將前面語句的結果做爲後面語句操做的數據源

 3. LINQ to Objects 

LINQ to ObjectsLINQ的精華部分。 

LINQ to Objects 提供對內存中集合操做的支持,由程序集System.Core.dllSystem.Linq命名空間下的Enumerable靜態類提供。 

 

這些擴展方法都是針對IEnumerable的對象進行擴展的也就是說,只要實現了IEnumerable接口,就可使用這些擴展方法

4.示例

 咱們說對於LINQ查詢語法,有兩種可供選擇,一種是擴展方法(又叫方法語法(Fluent Syntax),另外一種是查詢表達式(Query Expression。他們是等價的,只是寫法上不一樣。

在下面的示例中,咱們都會用兩種語法來完成。

編號

Id

姓名

Name

年齡

Age

門派

Menpai

武學

Kungfu

武學級別

Level

1

黃蓉

18

丐幫

打狗棒法

9

2

洪七公

70

丐幫

打狗棒法

10

3

郭靖

22

丐幫

降龍十八掌

10

4

任我行

50

明教

葵花寶典

1

5

東方不敗

35

明教

葵花寶典

10

6

林平之

23

華山

葵花寶典

7

7

嶽不羣

50

華山

葵花寶典

8

 

編號

KongfuId

武學名稱

KongfuName

殺傷力

Lethality

1

打狗棒法

90

2

降龍十八掌

95

3

葵花寶典

100

 如上對象「武林高手」(MartialArtsMaster)和「武學」(Kongfu 

 1 /// <summary>
 2 /// 類:武林高手
 3 /// MartialArtsMaster
 4 /// </summary>
 5 class  MartialArtsMaster
 6 {
 7     /// <summary>
 8     /// 編號
 9     /// </summary>
10     public int Id{get;set;}
11     /// <summary>
12     /// 姓名
13     /// </summary>
14     public string Name { get; set; }
15     /// <summary>
16     /// 年齡
17     /// </summary>
18     public int Age { get; set; }
19     /// <summary>
20     /// 門派
21     /// </summary>
22     public string Menpai { get; set; }
23     /// <summary>
24     /// 武學
25     /// </summary>
26     public string Kungfu { get; set; }
27     /// <summary>
28     /// 級別
29     /// </summary>
30     public int Level { get; set; }
31 }
32 
33 /// <summary>
34 /// 類:武學
35 /// Kongfu
36 /// </summary>
37 class Kongfu
38 {
39     /// <summary>
40     /// 武學編號
41     /// </summary>
42     public int KongfuId { get; set; }
43 
44     /// <summary>
45     /// 武學名稱
46     /// </summary>
47     public string KongfuName { get; set; }
48 
49     /// <summary>
50     /// 殺傷力
51     /// </summary>
52     public int Lethality { get; set; }
53 }

 

//初始化武林高手
var master = new List<MartialArtsMaster>(){ 
    new MartialArtsMaster(){ Id = 1, Name = "黃蓉",    Age = 18, Menpai = "丐幫", Kungfu = "打狗棒法",  Level = 9  },
    new MartialArtsMaster(){ Id = 2, Name = "洪七公",  Age = 70, Menpai = "丐幫", Kungfu = "打狗棒法",  Level = 10 },
    new MartialArtsMaster(){ Id = 3, Name = "郭靖",    Age = 22, Menpai = "丐幫", Kungfu = "降龍十八掌",Level = 10 },
    new MartialArtsMaster(){ Id = 4, Name = "任我行",  Age = 50, Menpai = "明教", Kungfu = "葵花寶典",  Level = 1  },
    new MartialArtsMaster(){ Id = 5, Name = "東方不敗",Age = 35, Menpai = "明教", Kungfu = "葵花寶典",  Level = 10 },
    new MartialArtsMaster(){ Id = 6, Name = "林平之",  Age = 23, Menpai = "華山", Kungfu = "葵花寶典",  Level = 7  },
    new MartialArtsMaster(){ Id = 7, Name = "嶽不羣",  Age = 50, Menpai = "華山", Kungfu = "葵花寶典",  Level = 8  }         
};
//初始化武學
var kongfu = new List<Kongfu>(){
    new Kongfu(){KongfuId=1, KongfuName="打狗棒法", Lethality=90},
    new Kongfu(){KongfuId=2, KongfuName="降龍十八掌", Lethality=95},
    new Kongfu(){KongfuId=3, KongfuName="葵花寶典", Lethality=100}
}

4.1過濾操做符

根據條件返回匹配元素的集合IEnumerable<T>。

1)  Where:根據返回bool值的Func委託參數過濾元素。

業務說明:查詢得到車手冠軍次數大於15次且是Austria國家的一級方程式賽手

2)  OfType<TResult>:接收一個非泛型的IEnumerable集合,根據OfType泛型類型參數過濾元素,只返回TResult類型的元素。

業務說明:過濾object數組中的元素,返回字符串類型的數組。

3)  Distinct:刪除序列中重複的元素。 

示例一:查詢 丐幫 中 修行"級別"高於 "8的大俠 

 1 //示例一:查詢 丐幫 中 修行"級別"高於 "8級" 的大俠
 2 
 3 //表達式 寫法
 4 var GaiBangMaster = from   m in master
 5         where  m.Level > 8 && m.Menpai == "丐幫"
 6         select m;
 7 
 8 //擴展方法 寫法
 9 var GaiBangMasterMethod = master.Where(m => m.Level > 8 && m.Menpai == "丐幫");
10 
11 //輸出結果
12 string GaiBangMasterResult="查詢\"丐幫\"中\"功力\"高於80的大俠(表達式寫法):\n";
13 GaiBangMasterResult +="編號(Id) 姓名(Name) 年齡(Age) 門派(Mengpai) 武學(Kungfu) 級別(Level)\n";
14 
15 foreach(var m in GaiBangMaster)
16   GaiBangMasterResult += m.Id + "  " + m.Name+"  "+m.Age+"  "+m.Menpai+"  "+m.Kungfu+"  "+m.Level+"  " + "\n";
17 Console.WriteLine(GaiBangMasterResult);
18 
19 string GaiBangMasterMethodResult = "查詢\"丐幫\"中\"功力\"高於80的大俠(擴展方法 寫法):\n";
20 GaiBangMasterMethodResult += "編號(Id) 姓名(Name) 年齡(Age) 門派(Mengpai) 武學(Kungfu) 級別(Level)\n";
21 
22 foreach (var m in GaiBangMasterMethod)
23   GaiBangMasterMethodResult += m.Id + "  " + m.Name + "  " + m.Age + "  " + m.Menpai + "  " + m.Kungfu + "  " + m.Level + "  " + "\n";
24 Console.WriteLine(GaiBangMasterMethodResult);
25 
26 Console.ReadKey();

 輸出結果以下:

4.2.  投影操做符

1)  Select 將序列的每一個元素通過lambda表達式處理後投影到一個新類型元素上。(與SelectMany不一樣在於,若單個元素投影到IEnumerable<TResult>,Select不會對多個IEnumerable<TResult>進行合併)

2) SelectMany

a)   c#編譯器會把「複合from子句」的查詢表達式轉換爲SelectMany()擴展方法。

b) 將序列的每一個元素通過lambda表達式處理後投影到一個 IEnumerable<TResult>,再將多個IEnumerable<TResult>序列合併爲一個返回序列IEnumerable<TResult>。

 c)  將序列的每一個元素通過lambda表達式處理後投影到一個 IEnumerable<TCollection>,再將多個IEnumerable<TCollection>序列合併爲一個返回序列IEnumerable<TCollection>,並對其中每一個元素調用結果選擇器函數。

 示例二:過濾所學」武功」 「傷殺力」 大於90 的大俠 

 1 //示例二:過濾所學」武功」 「傷殺力」 大於90 的大俠
 2 //表達式 寫法
 3 var masterKongfu = from m in master
 4                    from k in kongfu
 5                    where( k.Lethality > 90 && m.Kungfu==k.KongfuName)
 6                    orderby m.Level
 7                    select m.Id + "  " + m.Name + "  " + m.Age + "  " + m.Menpai + "  " + m.Kungfu + "  " + m.Level + "  ";
 8 //擴展方法 寫法
 9 var masterKongfuMethod = master
10                         .SelectMany(
11                            k => kongfu,
12                            (m, k) => new { mt = m, kf = k }
13                         )
14                         .Where(x =>x.kf.Lethality> 90 && x.mt.Kungfu==x.kf.KongfuName )                       
15                         .OrderBy(m => m.mt.Level)
16                         .Select(m => m.mt.Id + "  " + m.mt.Name + "  " + m.mt.Age + "  " + m.mt.Menpai + "  " + m.mt.Kungfu + "  " + m.mt.Level + "  ");
17 //輸出結果
18 string masterKongfuResult = "過濾所學」武功」 「傷殺力」 大於90 的大俠(表達式寫法):\n";
19 masterKongfuResult += "編號(Id) 姓名(Name) 年齡(Age) 門派(Mengpai) 武學(Kungfu) 級別(Level)\n";
20 foreach (var m in masterKongfu)
21     masterKongfuResult += m.ToString()+"  " + "\n";
22 Console.WriteLine(masterKongfuResult);
23 
24 string masterKongfuMethodResult = "過濾所學」武功」 「傷殺力」 大於90 的大俠(擴展方法 寫法):\n";
25 masterKongfuMethodResult += "編號(Id) 姓名(Name) 年齡(Age) 門派(Mengpai) 武學(Kungfu) 級別(Level)\n";
26 foreach (var m in masterKongfuMethod)
27     masterKongfuMethodResult += m.ToString() + "  " + "\n";
28 Console.WriteLine(masterKongfuMethodResult);
29 
30 Console.ReadKey();

 輸出結果以下:

4.3排序操做符

1)      OrderBy<TSource,TKey>,OrderByDescending<TSource,TKey>:根據指定鍵按升序或降序對集合進行第一次排序,輸出IOrderedEnumerable<TSource>。

2)        ThenBy<TSource,TKey>,ThenByDescending<TSource,TKey>:只會對那些在前一次排序中擁有相同鍵值的elements從新根據指定鍵按升序或降序排序。輸入IOrderedEnumerable <TSource>。

   3)      Reverse<TSource>:反轉集合中全部元素的順序。 

示例三:「華山論劍」之武林排行榜 (注:武功高低=所學武學殺傷力*我的武學級別) 排序規則,先按武功高低(由高到低),再按年齡(從小到大),再按名字首字母(正序)

 1 //示例三:「華山論劍」之武林排行榜 (注:武功高低=所學武學殺傷力*我的武學級別) 排序規則,先按武功高低(由高到低),再按年齡(從小到大),再按名字首字母(正序)
 2 //表達式寫法
 3 int i=0;
 4 var topMaster= from m in master
 5                     from k in kongfu
 6                 where (k.KongfuName==m.Kungfu)
 7                 orderby m.Level*k.Lethality descending,m.Age,m.Name
 8                 select m.Id + "  " + m.Name + "  " + m.Age + "  " + m.Menpai + "  " + m.Kungfu + "  " + m.Level + "  "+m.Level*k.Lethality +"  "+(++i);
 9 //輸出結果
10 string topMasterResult = "「華山論劍」之武林排行榜 (注:武功高低=所學武學殺傷力*我的武學級別)(表達式 寫法):\n";
11 topMasterResult += "編號  姓名  年齡  門派  武學  級別  武功  排名\n";
12 foreach (var m in topMaster)
13     topMasterResult += m.ToString() + "  " + "\n";
14 Console.WriteLine(topMasterResult);
15 
16 //擴展方法寫法
17 i = 0;
18 var topMasterMethod = master
19             .SelectMany(
20                 k => kongfu,
21                 (m, k) => new { mt = m, kf = k }
22             )
23             .Where(x => x.mt.Kungfu == x.kf.KongfuName)
24             .OrderByDescending(m => m.mt.Level*m.kf.Lethality)
25             .ThenBy(m=>m.mt.Age)
26             .ThenBy(m=>m.mt.Name)
27             .Select(m => m.mt.Id + "  " + m.mt.Name + "  " + m.mt.Age + "  " + m.mt.Menpai + "  " + m.mt.Kungfu + "  " + m.mt.Level + "  " + m.mt.Level * m.kf.Lethality + "  " + (++i));
28 //輸出結果
29 string topMasterMethodResult = "「華山論劍」之武林排行榜 (注:武功高低=所學武學殺傷力*我的武學級別)(擴展方法 寫法):\n";
30 topMasterMethodResult += "編號  姓名  年齡  門派  武學  級別  武功  排名\n";
31 foreach (var m in topMasterMethod)
32     topMasterMethodResult += m.ToString() + "  " + "\n";
33 Console.WriteLine(topMasterMethodResult);
34 
35 Console.ReadKey();

輸出結果以下:

4.4鏈接操做符

注意:join…on…關鍵字後的相等使用equals關鍵字。 

 1 //獲取集合MasterTop (武功我的級別大於8的高手)
 2 int ii = 1;
 3 var MasterTop = master
 4  .Where(x => x.Level > 8)
 5  .OrderByDescending(x => x.Level)
 6  .Select(x => new { Id = x.Id, Name = x.Name, MasterKongfu = x.Kungfu, Level = x.Level, Top = (ii++) });
 7                         
 8 
 9 //獲取集合KongfuTop (武功殺傷力大於90的武功)
10 ii=1;
11 var KongfuTop = from k in kongfu
12     where (k.Lethality>90)
13     orderby k.Lethality descending
14     select new{ KongfuId=k.KongfuId,KongfuName=k.KongfuName,Lethality=k.Lethality,KongfuTop=(ii++)};

在此以前,咱們先獲取兩個對象MasterTop (武功我的級別大於8的高手)和對象KongfuTop(武功殺傷力大於90的武功)

1) Join:基於匹配鍵對兩個序列的元素進行關聯。

示例三:經過對象MasterTop 和 KongfuTop關聯,返回新的對象 MasterLethalityTop高手殺傷力(包含高手的編號,名字,所學武功,級別,總殺傷力) 

 1 //示例三:經過對象MasterTop 和 KongfuTop關聯,返回新的對象 MasterLethalityTop高手殺傷力(包含高手的編號,名字,所學武功,級別,總殺傷力)
 2 //表達式
 3 var MasterLethalityTop = from m in MasterTop
 4                          join k in KongfuTop on m.MasterKongfu equals k.KongfuName
 5                          orderby m.Level*k.Lethality descending
 6                          select new
 7                          {
 8                              Id = m.Id,Name=m.Name,Kongfu=m.MasterKongfu,Level=m.Level,Kill=m.Level*k.Lethality
 9                          };
10 //擴展方法
11 var MasterLethalityTopMothod = MasterTop.Join(KongfuTop,
12                                     m => m.MasterKongfu,
13                                     k => k.KongfuName,
14                                    (m, k) => new
15                                    {
16                                        Id = m.Id,
17                                        Name = m.Name,
18                                        Kongfu = m.MasterKongfu,
19                                        Level = m.Level,
20                                        Kill = m.Level * k.Lethality
21                                    }
22                               )
23                               .OrderByDescending(m => m.Kill);
24 
25 Console.WriteLine("經過對象MasterTop 和 KongfuTop關聯,返回新的對象 MasterLethalityTop高手殺傷力(表達式):\n");
26 Console.WriteLine("編號     名字     所學武功     級別     總殺傷力\n");
27 foreach (var ma in MasterLethalityTop)
28     Console.WriteLine(ma.Id + "  " + ma.Name + "  " + ma.Kongfu + "  " + ma.Level + "  " + ma.Kill + "\n");
29 
30 Console.WriteLine("經過對象MasterTop 和 KongfuTop關聯,返回新的對象 MasterLethalityTop高手殺傷力(擴展方法):\n");
31 Console.WriteLine("編號     名字     所學武功     級別     總殺傷力\n");
32 foreach (var ma in MasterLethalityTopMothod)
33     Console.WriteLine(ma.Id + "  " + ma.Name + "  " + ma.Kongfu + "  " + ma.Level + "  " + ma.Kill + "\n");
34 Console.ReadKey();

運行結果以下:

 

2)   GroupJoin:基於鍵相等對兩個序列的元素進行關聯並對結果進行分組。常應用於返回「主鍵對象-外鍵對象集合」形式的查詢。

注意:直接出如今join子句以後的into關鍵字會被翻譯爲GroupJoin,而在select或group子句以後的into表示繼續一個查詢。

示例三: 使用武學關聯masterkongfu,並按武學分組;並按使用者數量排序 

 1 //示例三:過濾操做符GroupJoin 使用武學關聯master和kongfu,並按武學分組;並按使用者數量排序
 2             
 3 //增長几個武林高手
 4 master.Add(new MartialArtsMaster() { Id = 8, Name = "令狐沖", Age = 23, Menpai = "華山", Kungfu = "獨孤九劍", Level = 10 });
 5 master.Add(new MartialArtsMaster() { Id = 9, Name = "梅超風", Age = 23, Menpai = "桃花島", Kungfu = "九陰真經", Level = 8 });
 6 master.Add(new MartialArtsMaster() { Id =10, Name = "黃藥師", Age = 23, Menpai = "梅花島", Kungfu = "彈指神通", Level = 10 });
 7 master.Add(new MartialArtsMaster() { Id = 11, Name = "風清揚", Age = 23, Menpai = "華山", Kungfu = "獨孤九劍", Level = 10 });
 8 
 9 //增長几個武學
10 kongfu.Add(new Kongfu() { KongfuId=  4, KongfuName = "獨孤九劍", Lethality = 100 });
11 kongfu.Add(new Kongfu() { KongfuId = 5, KongfuName = "九陰真經", Lethality = 100 });
12 kongfu.Add(new Kongfu() { KongfuId = 6, KongfuName = "彈指神通", Lethality = 100 });
13 
14 Console.WriteLine("過濾操做符GroupJoin 使用武學關聯master和kongfu,並按武學分組 (表達式):\n");
15 Console.WriteLine("武學編號 名字 殺傷力 學會的大俠數\n");
16 var masterItems = from k in kongfu
17                   join m in master on k.KongfuName equals m.Kungfu
18                   into groups
19                   orderby groups.Count() descending
20                   select new
21                   {
22                     KongfuId = k.KongfuId,
23                     KongfuName = k.KongfuName,
24                     Lethality = k.Lethality,
25                     Count=groups.Count()
26                   };            
27 foreach(var ma in masterItems)
28 Console.WriteLine(ma.KongfuId + "  " + ma.KongfuName + "  " + ma.Lethality + "  " + ma.Count + "\n");
29 
30 
31 Console.WriteLine("過濾操做符GroupJoin 使用武學關聯master和kongfu,並按武學分組 (擴展方法):\n");
32 Console.WriteLine("武學編號 名字 殺傷力 學會的大俠數\n");
33 var masterItemsMothod = kongfu.GroupJoin(master,
34              k => k.KongfuName, m => m.Kungfu,
35              (k, m) => new { k.KongfuId, k.KongfuName, k.Lethality, Count = m.Count() }
36              )
37              .OrderByDescending(k => k.Count);
38            
39 foreach (var ma in masterItemsMothod)
40     Console.WriteLine(ma.KongfuId+"  "+ma.KongfuName + "  " + ma.Lethality +"  "+ ma.Count + "\n");

 輸出結果以下:

3) join…on…equals…支持多個鍵關聯

可使用匿名類型來對多個鍵值進行Join,以下所示:

                from x in sequenceX

                join y in sequenceY on new { K1 = x.Prop1, K2 = x.Prop2 }

                equals new { K1 = y.Prop3, K2 = y.Prop4 }

                ...

兩個匿名類型的結構必須徹底一致,這樣編譯器會把它們對應到同一個實現類型,從而使鏈接鍵值彼此兼容。

4)  Join與GroupJoin結果集對比 

Join操做符執行一個內鏈接(inner join), 輸出一個扁平序列

 

 1 //Join和GroupJoin區別
 2 
 3 Console.WriteLine("Join和GroupJoin區別,使用Join (表達式):\n");
 4 Console.WriteLine("武學編號 名字 殺傷力 使用大俠名字\n");
 5 var JoinOrGroupJoin = from k in kongfu
 6                   join m in master on k.KongfuName equals m.Kungfu                  
 7                   orderby k.KongfuId
 8                   select new
 9                   {
10                       KongfuId = k.KongfuId,
11                       KongfuName = k.KongfuName,
12                       Lethality = k.Lethality,
13                      Master= m.Name
14                   };
15 foreach (var ma in JoinOrGroupJoin)
16     Console.WriteLine(ma.KongfuId + "  " + ma.KongfuName + "  " + ma.Lethality + "  " + ma.Master + "\n");
17 
18 
19 Console.WriteLine("Join和GroupJoin區別,使用GroupJoin(擴展方法):\n");
20 Console.WriteLine("武學編號 名字 殺傷力 學會的大俠數\n");
21 var JoinOrGroupJoinMethod = kongfu.GroupJoin(master,
22              k => k.KongfuName, m => m.Kungfu,
23              (k, m) => new { k.KongfuId, k.KongfuName, k.Lethality, Count = m.Count() }
24              )
25              .OrderBy(k => k.KongfuId);
26 
27 foreach (var ma in JoinOrGroupJoinMethod)
28     Console.WriteLine(ma.KongfuId + "  " + ma.KongfuName + "  " + ma.Lethality + "  " + ma.Count + "\n");
29 
30 
31 Console.ReadKey();

 

運行結果以下:

 

5. 分組操做符 

1) 返回值爲 IEnumerable<IGrouping<TKey, TSource>> ,根據指定的鍵選擇器函數對序列中的元素進行分組。 

2)  返回值爲 IEnumerable<TResult>,根據指定的鍵選擇器函數對序列中的元素進行分組,而且從每一個組及其鍵中建立結果值。 

 示例五:按門派分類大俠,統計出各個門派大俠數量

 1 //分組Group
 2 var GroupItems=  from m in master                             
 3                  group m by m.Menpai into g
 4                  orderby g.Key
 5                  select new
 6                  {
 7                      Menpai = g.Key,         
 8                     Count= g.Count()
 9                  };
10 Console.WriteLine("分組Group(表達式):\n");
11 Console.WriteLine("門派 所在門派人數\n");
12 foreach (var ma in GroupItems)
13     Console.WriteLine(ma.Menpai + "  " + ma.Count + "\n");
14 
15 
16 var GroupItemsMethod = master.GroupBy(m => m.Menpai, (k, m) => new { Menpai = k, Count = m.Count() });
17 
18 Console.WriteLine("分組Group(擴展方法 寫法一):\n");
19 Console.WriteLine("門派 所在門派人數\n");
20 foreach (var ma in GroupItemsMethod)
21     Console.WriteLine(ma.Menpai + "  " + ma.Count + "\n");
22 
23 
24 var GroupItemsMethod2 = master.GroupBy(m => m.Menpai).Select((m) => new { Menpai = m.Key, Count = m.Count() });
25 
26 Console.WriteLine("分組Group(擴展方法 寫法二):\n");
27 Console.WriteLine("門派 所在門派人數\n");
28 foreach (var ma in GroupItemsMethod2)
29     Console.WriteLine(ma.Menpai + "  " + ma.Count + "\n");
30 
31 Console.ReadKey();

運行結果以下:

 6.量詞操做符

若是元素序列知足指定的條件,量詞操做符就返回布爾值。

1)Any:肯定序列是否包含任何元素;或肯定序列中的任何元素是否都知足條件。

2)All:肯定序列中的全部元素是否知足條件。

3)Contains:肯定序列是否包含指定的元素。

學到這裏,再學習量詞操做符,就至關簡單了,直接看代碼:

 1 //量詞操做符(Any.All,Contains)
 2 //先初始化一個對象備用
 3 var AnyItems = from m in master
 4                where m.Kungfu == "葵花寶典"
 5                select new { m.Name, m.Menpai, m.Kungfu };
 6 
 7 Console.WriteLine("練葵花寶典的全部大俠名單\n");
 8 Console.WriteLine("名字     門派   研習武學\n");
 9 foreach (var m in AnyItems)
10     Console.WriteLine(m.Name + "   " + m.Menpai + "   " + m.Kungfu+"\n");
11 //使用Any (肯定序列是否包含任何元素;或肯定序列中的任何元素是否都知足條件)。
12 var any = AnyItems.Any(m => m.Menpai == "明教");
13 Console.WriteLine("是否研習葵花寶典有明教中人?"+any);
14 //返回True,說練習葵花寶典的大俠中有明教的人
15 
16 //All(肯定序列中的全部元素是否知足條件)
17 var all = AnyItems.All(m => m.Menpai == "明教");
18 Console.WriteLine("是否研習葵花寶典的全是明教中人?"+all);
19 
20 //Contains(肯定序列中是否包含指定的元素)
21 //先聲明兩個元素
22 var OuYangFeng = new MartialArtsMaster() { Id = 13, Name = "歐陽鋒", Age = 50, Menpai = "白駝山莊", Kungfu = "蛤蟆功", Level = 10 };
23 var HuanYaoShi = master[9];
24 //使用Contains判斷是否存在
25 var IsOuYangFeng = master.Contains(OuYangFeng);
26 var IsHuangYaoShi = master.Contains(HuanYaoShi as MartialArtsMaster);
27 Console.WriteLine("大俠名單中是否存在歐陽鋒?" + IsOuYangFeng);
28 Console.WriteLine("大俠名單中是否存在黃藥師?" + IsHuangYaoShi);
29 Console.ReadKey();

運行結果以下:

 7.分區操做符

須要放在查詢的「最後」,返回集合的一個子集。

1)        Take:從序列的開頭返回指定數量的連續元素。

2)        TakeWhile:只要知足指定的條件,就會返回序列的元素。

3)        Skip:跳過序列中指定數量的元素,而後返回剩餘的元素。

4)        SkipWhile:只要知足指定的條件,就跳過序列中的元素,而後返回剩餘元素。

實例:咱們使用分區操做符對各位大俠進行分頁(每頁5個)

 1 //分區操做符
 2 //咱們在操做以前,先增長一些元素
 3 //增長几個武林高手
 4 master.Add(new MartialArtsMaster() { Id = 12, Name = "肖峯", Age = 33, Menpai = "丐幫", Kungfu = "降龍十八掌", Level = 9 });
 5 master.Add(new MartialArtsMaster() { Id = 13, Name = "段譽", Age = 23, Menpai = "天龍寺", Kungfu = "六脈神劍", Level = 7 });
 6 master.Add(new MartialArtsMaster() { Id = 14, Name = "虛竹", Age = 26, Menpai = "逍遙派", Kungfu = "北冥神功", Level = 9 });
 7 master.Add(new MartialArtsMaster() { Id = 15, Name = "方正大師", Age = 23, Menpai = "少林寺", Kungfu = "七十二絕技", Level = 10 });
 8 master.Add(new MartialArtsMaster() { Id = 16, Name = "楊過", Age = 23, Menpai = "古墓派", Kungfu = "玉女心經", Level = 10 });
 9 master.Add(new MartialArtsMaster() { Id = 17, Name = "小龍女", Age = 23, Menpai = "古墓派", Kungfu = "玉女心經", Level = 10 });
10 
11 //增長几個武學
12 kongfu.Add(new Kongfu() { KongfuId = 7, KongfuName = "六脈神劍", Lethality = 100 });
13 kongfu.Add(new Kongfu() { KongfuId = 8, KongfuName = "北冥神功", Lethality = 100 });
14 kongfu.Add(new Kongfu() { KongfuId = 9, KongfuName = "七十二絕技", Lethality = 100 });
15 kongfu.Add(new Kongfu() { KongfuId = 10, KongfuName = "玉女心經", Lethality = 95 });
16 
17 int pageSize = 5; //每頁數量 5
18 int pageNumber = (int)Math.Ceiling(master.Count() / (double)pageSize); //計算總頁數
19 Console.WriteLine("使用分區操做符進行分頁\n");
20 Console.WriteLine("大俠總數:" + master.Count() + "    總頁數:" + pageNumber + "    每頁:" + pageSize + "\n");
21 for (int i = 0; i < pageNumber;i++ )
22 {
23     Console.WriteLine("*************************第:" + (i+1).ToString() + "頁*************************\n");
24     var pageMaster = (
25                        from m in master
26                        join k in kongfu on m.Kungfu equals k.KongfuName
27                        orderby m.Level * k.Lethality descending
28                        select new { m.Name, m.Menpai, m.Kungfu, k.Lethality, m.Level, Kill = m.Level * k.Lethality }
29                     ).Skip(i * pageSize).Take(pageSize);
30     Console.WriteLine("姓名    門派     武功   武功殺傷力  修煉等級  總武力\n");
31     foreach (var m in pageMaster)
32         Console.WriteLine(m.Name + " " + m.Menpai + " " + m.Kungfu + " " + m.Lethality + " " + m.Level + " " + m.Kill + "\n");
33     Console.WriteLine();
34 }

運行結果以下:

8.集合操做符

1)Union:並集,返回兩個序列的並集,去掉重複元素。

2)Concat:並集,返回兩個序列的並集。

3)Intersect:交集,返回兩個序列中都有的元素,即交集。

4)Except:差集,返回只出如今一個序列中的元素,即差集。

 實例:華山派和明教中都使用葵花寶典的大俠

 1 //集合操做符
 2 //實例:華山派和明教中都使用葵花寶典的大俠
 3 var ItemsIntersect = (
 4              from m in master
 5              where m.Menpai == "華山" || m.Menpai=="明教"
 6              select m
 7            ).Intersect(
 8              from m in master
 9              where m.Kungfu == "葵花寶典"
10              select m
11            );
12 foreach (var m in ItemsIntersect)
13     Console.WriteLine(m.Name + " " + m.Menpai + " " + m.Kungfu);

運行結果以下:

9. 元素操做符

這些元素操做符僅返回一個元素,不是IEnumerable<TSource>。(默認值:值類型默認爲0,引用類型默認爲null)

1)First:返回序列中的第一個元素;若是是空序列,此方法將引起異常。

2)FirstOrDefault:返回序列中的第一個元素;若是是空序列,則返回默認值default(TSource)。

3)Last:返回序列的最後一個元素;若是是空序列,此方法將引起異常。

4)LastOrDefault:返回序列中的最後一個元素;若是是空序列,則返回默認值default(TSource)。

5)Single:返回序列的惟一元素;若是是空序列或序列包含多個元素,此方法將引起異常。

6)SingleOrDefault:返回序列中的惟一元素;若是是空序列,則返回默認值default(TSource);若是該序列包含多個元素,此方法將引起異常。

7)ElementAt:返回序列中指定索引處的元素,索引從0開始;若是索引超出範圍,此方法將引起異常。

8)ElementAtOrDefault:返回序列中指定索引處的元素,索引從0開始;若是索引超出範圍,則返回默認值default(TSource)。

10.  合計操做符

1)Count:返回一個 System.Int32,表示序列中的元素的總數量。

2)LongCount:返回一個 System.Int64,表示序列中的元素的總數量。

3)Sum:計算序列中元素值的總和。

4)Max:返回序列中的最大值。

5)Min:返回序列中的最小值。

6)Average:計算序列的平均值。

7)Aggregate:對序列應用累加器函數。

      Aggregate比較複雜,因此只列出Aggregate示例。

      Aggregate的第一個參數是算法的種子,即初始值。第二個參數是一個表達式,用來對每一個元素進行計算(委託第一個參數是累加變量,第二個參數當前項)。第三個參數是一個表達式,用來對最終結果進行數據轉換。

示例:

1 int[] numbers = { 1, 2, 3 };
2 // 1+2+3 = 6
3 int y = numbers.Aggregate((prod, n) => prod + n);
4 // 0+1+2+3 = 6
5 int x = numbers.Aggregate(0, (prod, n) => prod + n);
6 // (0+1+2+3)*2 = 12
7 int z = numbers.Aggregate(0, (prod, n) => prod + n, r => r * 2);

11.轉換操做符

1)Cast:將非泛型的 IEnumerable 集合元素轉換爲指定的泛型類型,若類型轉換失敗則拋出異常。

2)ToArray:從 IEnumerable<T> 建立一個數組。

3)ToList:從 IEnumerable<T> 建立一個 List<T>。

4)ToDictionary:根據指定的鍵選擇器函數,從 IEnumerable<T> 建立一個 Dictionary<TKey,TValue>。

5)ToLookup:根據指定的鍵選擇器函數,從 IEnumerable<T> 建立一個 System.Linq.Lookup<TKey,TElement>。

6)DefaultIfEmpty:返回指定序列的元素;若是序列爲空,則返回包含類型參數的默認值的單一元素集合。

7)   AsEnumerable:返回類型爲 IEnumerable<T> 。用於處理LINQ to Entities操做遠程數據源與本地集合的協做。

12. 生成操做符

生成操做符返回一個新的集合。(三個生成操做符不是擴展方法,而是返回序列的正常靜態方法)

1 ) Empty:生成一個具備指定類型參數的空序列 IEnumerable<T>。

2)  Range:生成指定範圍內的整數的序列 IEnumerable<Int32>。

3)  Repeat:生成包含一個重複值的序列 IEnumerable<T>。 

13.  Linq to Objects的延遲計算

Linq查詢的延遲計算原理:經過給LINQ擴展方法傳遞方法委託,做爲yield迭代器的主體,讓遍歷執行到MoveNext()時才執行耗時的指令。

 整理Linq to Objects中運算符延遲計算特性

按字母順序整理:

具備延遲計算的運算符

Cast,Concat,DefaultIfEmpty,Distinct,Except,GroupBy,GroupJoin,Intersect 
,Join,OfType,OrderBy,OrderByDescending,Repeat,Reverse,Select,SelectMany,Skip
,SkipWhile,Take,TakeWhile,ThenBy,ThenByDescending,Union,Where,Zip

當即執行的運算符

Aggregate,All,Any,Average,Contains,Count,ElementAt,ElementAtOrDefault 
,Empty,First,FirstOrDefault,Last,LastOrDefault,LongCount,Max,Min,Range 
,SequenceEqual,Single,SingleOrDefault,Sum,ToArray,ToDictionary,ToList,ToLookup

特殊的AsEnumerable運算符,用於處理LINQ to Entities操做遠程數據源,將IQueryable遠程數據當即轉化爲本地的IEnumerable集合。若AsEnumerable接收參數是IEnumerable內存集合則什麼都不作。

 14.總結

在寫這節的時候,參考了很多博客園還有網上的貼子,在此表示感謝。

LINQ學習還只是開始,可是有了上面兩節的學習,咱們再進行深刻學習,就不會不知所措了。

本節主要學習了LINQ to Objects在查詢中的使用方法。

 ============================================================================================== 

返回目錄

 <若是對你有幫助,記得點一下推薦哦,有不明白的地方或寫的不對的地方,請多交流>
 

QQ羣:467189533

==============================================================================================  

相關文章
相關標籤/搜索