C#沉澱-Linq的使用

Linq 可以輕鬆的查詢物件集合。Linq代表 語言整合查詢 ,是 .NET框架的擴充套件 ,支援從 資料庫、程式物件的集合以及XML文件 中查詢資料
一個簡單的示例:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForLinq { class Program { static void Main(string[] args) { //建立一個int陣列,作為被查詢的資料來源 int[] numbers = { 1, 2, 3, 4, 5, 18 }; //Linq定義查詢,注意,這裡只是“定義”而已 IEnumerable<int> lowNum = from nu in numbers where nu < 10 select nu; //遍歷lowNum,只有使用lowNum的時候,資料才會被查詢出來 //所以,在這裡才被執行了查詢 foreach (int item in lowNum) { Console.WriteLine(item); } Console.ReadKey(); } } }
針對於不同的資料來源,需要實現相應的Linq查詢的程式碼模組,這些程式碼模組被稱作 Linq提供程式 。在C#中,覺的Linq提供程式有 Linq to Object/ Linq to XML/ BLinq(Asp.Net) 等
匿名類
在深入瞭解Linq之前,需要先了解一下匿名類,因為在使用Linq語句的時候,會大量的使用匿名類
示例:使用匿名型別建立一個學生類
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForLinq { class Program { static void Main(string[] args) { //建立一個學生類 var student = new { Name = "Jack", Age = 18, Class = "013" }; Console.ReadKey(); } } }
解析:
建立一個匿名類需要用到關鍵字 new ,然後直接在後面跟 { } 來初始化類中成員(屬性),多個成員之間使用逗號分隔;因為沒有指定型別,所有在接收建立的物件時,需要用到關鍵字 var ,而且必須使用 var 關鍵字
- 匿名型別只能和區域性變數配合使用,不能用於類成員
- 由於匿名類沒有名字,所以必須以 var 關鍵字作為變數型別
- 不能設定匿名型別物件的屬性,因為匿名類的成員是 只讀的
在初始化一個匿名型別物件時,其成員的初始化不僅可以使用 賦值操作 ,還可以使用 成員訪問表示式 和 識別符號形式
示例:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForLinq { class Other { public static string Name = "Bob"; } class Program { static void Main(string[] args) { //區域性變數,表示班級 string Class = "013"; //建立一個學生類 var student = new { Other.Name, Age = 18, Class }; //訪問學生類中成員 Console.WriteLine("My name is "+student.Name); Console.WriteLine("I'm "+student.Age+" years old"); Console.WriteLine("My Class is " + student.Class); Console.ReadKey(); } } }
var student = new { Other.Name, Age = 18, Class };
的效果等同於 var student = new { Name = Other.Name, Age = 18, Class = Class};
如果再宣告一個具有相同的引數名、相同的推斷型別和相同順序的匿名型別的話,編譯器會重用這個型別直接建立新的例項,而不會建立新的匿名型別
方法語法和查詢語法
查詢語法:看上去和SQL語句很相似,使用查詢表達形式書寫
方法語法:使用標準的方法呼叫
查詢語法是 宣告式 的,但未指明如何執行這個查詢;方法語法是 命令式 的,它指明瞭方法查詢呼叫的順序
編譯器會將使用語法表示的查詢翻譯為方法呼叫的形式,在執行時這兩種方式沒有效能上的差異
先看方法語法與查詢語法的示例:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForLinq { class Other { public static string Name = "Bob"; } class Program { static void Main(string[] args) { //建立一個int陣列,作為被查詢的資料來源 int[] numbers = { 1, 2, 3, 4, 5, 18 }; //查詢語法 var _numbers = from nu in numbers where nu < 10 select nu; //方法語法,Where方法的引數使用了Lambda表示式 var _num = numbers.Where(x => x < 10); foreach (int item in _numbers) { Console.WriteLine(item); } foreach (int item in _num) { Console.WriteLine(item); } Console.ReadKey(); } } }
查詢變數
Linq查詢返回的結果可以是 一個列舉 ,也可以是一個叫做 標量 的單一值
示例:
//建立一個int陣列,作為被查詢的資料來源 int[] numbers = { 1, 2, 3, 4, 5, 18 }; //返回一個IEnumerable結果,它可以列舉返回的結果 IEnumerable<int> _numbers = from nu in numbers where nu < 10 select nu; //通過Count()方法返回查詢結果總數量 int _count = (from nu in numbers where nu < 10 select nu).Count();
等號左邊的變數叫做 查詢變數 ,這裡指 _numbers
和 _count
查詢變數一般使用 var 型別來讓編譯器自動推斷其返回的型別
如果查詢語句返回的是列舉型別,查詢變數中是不會包含結果的,只有在真正使用列舉值的時候才會執行查詢,並且每次使用列舉值的時候都會執行一次查詢語句;而如果查詢語句返回的是標題,查詢則立即生效,並把結果儲存在查詢變數中
查詢表示式的結構
from子名指定資料來源,並且引入迭代變數;迭代變數逐個表示資料來源的每一個元素;語法如下:
from [Type] item in Items
Items 表示資料來源; item 表示資料來源中的元素; Type 是可選的,表示元素的型別
join子句,聯結語句可以結合兩個或多個集合中的資料,然後產生一個臨時的 物件集合 ,每個集合中都包含原始集合物件中的所有元素,語法如下:
join Identifier in Collection2 on Field1 equqls Field2
示例:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForLinq { class Course//課程類 { public int ID; public int Student_ID; public string Course_Name; } class Student//學生類 { public int ID; public string Name; } class Program { static void Main(string[] args) { //學生類集合 Student[] st = new Student[] { new Student{ID=111,Name="Bob"}, new Student{ID=112,Name="Jack"}, new Student{ID=113,Name="Hong"} }; //課程類集合 Course[] co = new Course[] { new Course{ID=1, Student_ID=111,Course_Name="數學"}, new Course{ID=2, Student_ID=112,Course_Name="語文"}, new Course{ID=3, Student_ID=113,Course_Name="化學"}, new Course{ID=4, Student_ID=112,Course_Name="數學"}, new Course{ID=5, Student_ID=112,Course_Name="生物"} }; //Linq查詢語法 var result = from a in st //指定第一個資料來源st join b in co on a.ID equals b.Student_ID //聯結第二個資料來源ot,並用on指定聯結條件,equals來指定比較欄位 where b.Course_Name=="數學" //匹配數學課程 select a.Name; //返回名字 foreach (var name in result) { Console.WriteLine("參加數學課程的學生名:"+name); } Console.ReadKey(); } } }
from...let...where片段
可以使用多個 from 子句指定多個數據源,示例:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForLinq { class Course//課程類 { public int ID; public int Student_ID; public string Course_Name; } class Student//學生類 { public int ID; public string Name; } class Program { static void Main(string[] args) { //學生類集合 Student[] st = new Student[] { new Student{ID=111,Name="Bob"}, new Student{ID=112,Name="Jack"}, new Student{ID=113,Name="Hong"} }; //課程類集合 Course[] co = new Course[] { new Course{ID=1, Student_ID=111,Course_Name="數學"}, new Course{ID=2, Student_ID=112,Course_Name="語文"}, new Course{ID=3, Student_ID=113,Course_Name="化學"}, new Course{ID=4, Student_ID=112,Course_Name="數學"}, new Course{ID=5, Student_ID=112,Course_Name="生物"} }; //指定多個數據源 var st_co = from a in st from b in co where a.ID == b.Student_ID && b.Course_Name=="數學" select new { a.Name, b.Course_Name };//建立一個匿名型別物件 //訪問返回集體中的成員 foreach (var item in st_co) { Console.WriteLine("學生:"+item.Name); Console.WriteLine("課程:"+item.Course_Name); } Console.ReadKey(); } } }
let子句接受一個表示式的運算,並且把它賦值給一個需要在其它地方運算中使用的識別符號
示例:
//定義兩個資料來源 int[] number1 = { 1, 2, 3, 4, 5 }; int[] numbers2 = { 1, 2, 3, 4, 5, 18 }; var nu_array = from a in number1 from b in numbers2 let sum = a + b //使用let子句將第一個集合中的元素與第二個集合中的元素進行相加 where sum == 4 select new { a, b, sum }; foreach (var item in nu_array) { Console.WriteLine(item.a + "," + item.b + "," + item.sum); }
where子句根據之後運算來去除不符合指定條件的項,在 from...let...where 片段中可以有任意多個 where 子句
示例:
//定義兩個資料來源 int[] number1 = { 1, 2, 3, 4, 5 }; int[] numbers2 = { 1, 2, 3, 4, 5, 18 }; var nu_array = from a in number1 from b in numbers2 let sum = a + b //使用let子句將第一個集合中的元素與第二個集合中的元素進行相加 where sum == 4 //篩選a+b等於4的所有元素 where a == 2 //再指定a必須等於2,那返回的結果中,b就只能是等於2了 select new { a, b, sum }; foreach (var item in nu_array) { Console.WriteLine(item.a + "," + item.b + "," + item.sum); }
orderby子句
orderby子句接受一個表示式,並根據表示式按順序返回結果;排列的表示式也可以是集合中的成員
- orderby子句預設是按升序排列的;可以使用 ascending 顯示的指定為升序或使用 descending 指定為隆序
- 可以有任意多個子句,之間使用逗號分隔
示例:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForLinq { class Course//課程類 { public int ID; public int Student_ID; public string Course_Name; } class Student//學生類 { public int ID; public string Name; public int Age; } class Program { static void Main(string[] args) { //學生類集合 Student[] st = new Student[] { new Student{ID=111,Name="Bob",Age=12}, new Student{ID=112,Name="Jack",Age=15}, new Student{ID=113,Name="Hong",Age=9} }; //課程類集合 Course[] co = new Course[] { new Course{ID=1, Student_ID=111,Course_Name="數學"}, new Course{ID=2, Student_ID=112,Course_Name="語文"}, new Course{ID=3, Student_ID=113,Course_Name="化學"}, new Course{ID=4, Student_ID=112,Course_Name="數學"}, new Course{ID=5, Student_ID=112,Course_Name="生物"} }; var query = from student in st orderby student.Age // 根據Age欄位進行排序 select student; foreach (var item in query) { Console.WriteLine(string.Format("ID:{0},名字:{1},年齡:{2}",item.ID,item.Name,item.Age)); } Console.ReadKey(); } } }
select...group子句
select子句指定所選物件的哪部分應該被選擇;指定的部分可以是整個資料項,或資料項的一個欄位,或資料項的幾個欄位組成的新的物件
group by子句是可選的,用來指定選擇的項如何分組
select子句示例:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForLinq { class Student//學生類 { public int ID; public string Name; public int Age; } class Program { static void Main(string[] args) { //學生類集合 Student[] st = new Student[] { new Student{ID=111,Name="Bob",Age=12}, new Student{ID=112,Name="Jack",Age=15}, new Student{ID=113,Name="Hong",Age=9} }; var query = from student in st //select student.Name //選擇一個欄位 //select new {student.Name, student.Age} //選擇多個欄位組成的新物件 select student;// 選擇所有的sutdent元素 foreach (var item in query) { Console.WriteLine(string.Format("ID:{0},名字:{1},年齡:{2}",item.ID,item.Name,item.Age)); } Console.ReadKey(); } } }
查詢中的匿名類——查詢結果可以由原始集合的項、項的某些欄位或匿名型別組成,例如 select new {student.Name, student.Age}
group子句將select的物件根據一些標準進行分組
- 如果項包含在查詢語句中,它就可以根據某個欄位的值進行分組;作為分組的依據的屬性叫做 健(key)
- gorup將返回可以列舉已經形成的項的分組的可列舉型別
- 分組本身是可被列舉的
示例:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForLinq { class Student//學生類 { public int ID; public string Name; public int Age; } class Program { static void Main(string[] args) { //學生類集合 Student[] st = new Student[] { new Student{ID=111,Name="Bob",Age=12}, new Student{ID=112,Name="Jack",Age=15}, new Student{ID=113,Name="Json",Age=15}, new Student{ID=114,Name="Hong",Age=9} }; var query = from student in st group student by student.Age; //按照年齡來分組 foreach (var item in query) { Console.WriteLine("年齡組:"+item.Key); //通過Key來找到分組的依據 foreach (var it in item) { Console.WriteLine("\t名字:"+it.Name); } } Console.ReadKey(); } } }
查詢延續:into子句
查詢延續子句可以接受查詢的一部分結果並賦予一個名字,從而可以在查詢的另一部分中使用
示例:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForLinq { class Course//課程類 { public int ID; public int Student_ID; public string Course_Name; } class Student//學生類 { public int ID; public string Name; public int Age; } class Program { static void Main(string[] args) { int[] number1 = { 1, 2, 3, 4, 5 }; int[] numbers2 = { 1, 2, 3, 4, 5, 18 }; var result = from a in number1 join b in numbers2 on a equals b into a_b //通過into將number1與numbers2聯合命名為a_b from c in a_b select c; foreach (var item in result) { Console.WriteLine(item); } Console.ReadKey(); } } }
標準查詢運算子
- 被查詢的物件叫做序列,它必須實現 IEnumberable<T> 介面
- 標準查詢運算子使用方法語法
- 一些運算子返回 IEnumberable 物件,而其他的一些 運算子返回標量
- 很多操作都可以一個Lambda表示式做為引數
示例:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForLinq { class Course//課程類 { public int ID; public int Student_ID; public string Course_Name; } class Student//學生類 { public int ID; public string Name; public int Age; } class Program { static void Main(string[] args) { int[] number = { 1, 2, 3, 4, 5 }; //total與hwoMay都是標量 //被操作的物件number是一個序列 //而Sum()與Count()是去處符(方法) int total = number.Sum(); int howMany = number.Count(); Console.ReadKey(); } } }
序列是指實現了IEnumberable<T>介面的類,包括List<>/Dictionary<>/Stack<>/Array等待
共有47個標題運算子,下面列舉幾個常用的運算子:
- Where -- 根據給定的條件對序列進行過濾
- Select -- 指定要包含一個物件或
- Join -- 對兩個序列執行內聯結
- GroupBy -- 分組序列中的元素
- Dinstinct -- 去除序列中的重複項
- ToList -- 將序列作為List<T>返回
- First -- 返回序列中第一個與條件相匹配的元素
- FirstOrDefault -- 返回序列中第一個與條件相匹配的元素,如果匹配不到,就返回第一個元素
- Last -- 返回序列中最後一個與條件相匹配的元素
- LastOrDefault -- 返回序列中最後一個與條件相匹配的元素,如果匹配不到,就返回最後一個元素
- Count -- 返回序列中元素的個數
- Sum -- 返回序列中值的總和
- Min -- 返回序列中值的最小值
- Max -- 返回序列中值的最大值
- Average -- 返回序列中值的平均值
- Contains -- 返回一個布林值,指明序列中是否包含某個元素
System.Linq.Enumberable類聲明瞭標準查詢運算子方法,它們都擴充套件了 IEnumberable<T> 泛型類的擴充套件方法
- 由於運算子是泛型方法, 因此每個方法名都具有相關泛型引數(T)
- 由於運算子是擴充套件 IEnumberable 的擴充套件方法,它們必須滿足以下的語法條件
- 宣告為Public和Static
- 在第一個引數前有this指示器
- 把 IEnumberable<T> 作為第一個引數型別
示例:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForLinq { class Program { static void Main(string[] args) { int[] number = new int[] { 1, 2, 3, 4, 5 }; //方法語法,陣列作為引數 var _count = Enumerable.Count(number); var _first = Enumerable.First(number); //擴充套件語法,陣列被做為被擴充套件的物件 var __count = number.Count(); var __first = number.First(); Console.ReadKey(); } } }
每一個查詢表示式都會被編譯器翻譯成標準查詢運算子的形式,兩者可以結合使用,示例:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForLinq { class Program { static void Main(string[] args) { int[] number = new int[] { 1, 2, 3, 4, 5 }; int _num = (from nu in number where nu < 4 select nu).Count(); Console.ReadKey(); } } }