LINQ標准查詢運算法是依靠一組擴展方法來實現的。而這些擴展方法分別在System.Linq.Enumerable和System.Linq.Queryable這連個靜態類中定義。
Enumerable的擴展方法采用線性流程,每個運算法會被線性執行。這種執行方法如果操作類似關系型數據庫數據源,效率會非常低下,所以Queryable重新定義這些擴展方法,把LINQ表達式拆解為表達式樹,提供程序就可以根據表達式樹生成關系型數據庫的查詢語句,即SQL命令,然后進行相關操作。
每個查詢運算符的執行行為不同,大致分為立即執行和延時執行。延時執行的運算符將在枚舉元素的時候被執行。
Enumerable類位於程序集System.Core.dll中,System.Linq命名空間下,並且直接集成自System.Object,存在於3.5及以上的.NET框架中。Enumerable是靜態類,不能實例化和被繼承,其成員只有一組靜態和擴展方法。
LINQ不僅能夠查詢實現IEnumerable<T>或IQueryable<T>的類型,也能查詢實現IEnumerable接口的類型。關於Enumerable方法的詳細說明,請參考MSDN Enumerable 類
“--------------------------------------------------------------
理解LINQ首先必須理解擴展方法
msdn是這樣規定擴展方法的:“擴展方法被定義為靜態方法,但它們是通過實例方法語法進行調用的。 它們的第一個參數指定該方法作用於哪個類型,並且該參數以 this 修飾符為前綴。”
下面給個擴展方法的例子如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 擴展方法 { /// <summary> /// 為string類型定義一個擴展方法 /// </summary> static class Helper { public static string MyExtenMethod(this string s) { return s.Substring(0, 2); } } class Program { static void Main(string[] args) { string s = "擴展方法示例"; Console.WriteLine(s.MyExtenMethod());//調用 Console.ReadKey(false); } } }
程序的運行結果如下:
-----插曲,想到了就加進來,有助於理解開頭的幾段話及LINQ原理
參考DebugLZQ前面的博文:淺析EF涉及的一些C#語言特性
---------------------------------------------------------------”
為了方便理解和記憶,DebugLZQ將常用的延時執行的Enumerable類方法成員分了下組,具體如下:
1.Take用於從一個序列的開頭返回指定數量的元素
2.TakeWhile 用於獲取指定序列從頭開始符合條件的元素,直到遇到不符合條件的元素為止
3.Skip跳過序列中指定數量的元素
4.SkipWhile 用於跳過序列總滿足條件的元素,然會返回剩下的元素
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 延時執行的Enumerable類方法 { /// <summary> /// 延時執行的Enumerable類方法 /// DebugLZQ /// http://www.cnblogs.com/DebugLZQ /// </summary> class Program { static void Main(string[] args) { string[] names = { "DebugLZQ","DebugMan","Sarah","Jerry","Tom","Linda","M&M","Jeffery"}; //1.Take用於從一個序列的開頭返回指定數量的元素 // //a.在數組上直接使用Take方法 foreach (string name in names.Take(3)) { Console.Write("{0} ", name); } Console.WriteLine(); Console.WriteLine("-----"); //b.在LINQ返回的IEnumerable<T>序列上使用Take方法 var query = from string name in names where name.Length <=3 select name; foreach (string name in query.Take(1)) { Console.Write("{0} ",name); } Console.WriteLine(); Console.WriteLine("----------------------------"); Console.ReadKey(false); //2.TakeWhile 用於獲取指定序列從頭開始符合條件的元素,直到遇到不符合條件的元素為止 // var takenames = names.TakeWhile(n => n.Length>4); var takenames2 = names.TakeWhile((n,i)=>n.Length<10&&i<3); foreach (string name in takenames) { Console.Write("{0} ", name); } Console.WriteLine(); Console.WriteLine("-----"); foreach (string name in takenames2) { Console.Write("{0} ", name); } Console.WriteLine(); Console.WriteLine("----------------------------"); Console.ReadKey(false); //3.Skip跳過序列中指定數量的元素 // foreach (string name in names.Skip(5)) { Console.Write("{0} ", name); } Console.WriteLine(); Console.WriteLine("-----"); var query_skip = (from name in names where name.Length >= 3 select name).Skip(2); foreach (string name in query_skip.Skip(2) ) { Console.Write("{0} ", name); } Console.WriteLine(); Console.WriteLine("----------------------------"); Console.ReadKey(false); //4.SkipWhile 用於跳過序列總滿足條件的元素,然會返回剩下的元素 //跳過名字長度大於3的 var takenames_SkipWhile = names.SkipWhile(n => n.Length >3); foreach (string name in takenames_SkipWhile) { Console.Write("{0} ", name); } Console.WriteLine(); Console.WriteLine("-----"); var takenames_SkipWhile2 = names.SkipWhile((n,i)=>n.Length>3&&i>2); foreach (string name in takenames_SkipWhile2) { Console.Write("{0} ", name); } Console.WriteLine(); Console.WriteLine("----------------------------"); Console.ReadKey(false); //小結Take、Skip獲得第N到第M個元素 var names_TakeAndSkip = names.Skip(5).Take(3); var names_TakeAndSkip2 = (from name in names select name).Skip(5).Take(3); foreach (string name in names_TakeAndSkip) { Console.Write("{0} ", name); } Console.WriteLine(); Console.WriteLine("-----"); foreach (string name in names_TakeAndSkip2) { Console.Write("{0} ", name); } Console.WriteLine(); Console.WriteLine("----------------------------"); Console.ReadKey(false); } } }
程序中有詳細的注釋不再多做說明,程序運行結果如下:
5.Reverse用於翻轉序列中的元素的順序
6.Distinct過濾掉重復的元素
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Reverse_Distinct等 { /// <summary> /// DebugLZQ /// http://www.cnblogs.com/DebugLZQ /// </summary> class Program { static void Main(string[] args) { string[] names = { "DebugLZQ", "Jerry", "Sarah", "Jerry", "Tom", "Linda", "M&M", "Jeffery" }; //5.Reverse用於翻轉序列中的元素的順序 string str = "反轉字符串"; var strre = str.ToCharArray().Reverse(); var takenames = names.Reverse(); foreach (var c in strre) { Console.Write(c); } Console.WriteLine(); Console.WriteLine("-----"); foreach (var c in takenames ) { Console.WriteLine(c); } Console.WriteLine("----------------------------"); Console.ReadKey(false); //6.Distinct 過濾掉重復的元素 var takenames_Distinct = names.Distinct(); foreach (var c in takenames_Distinct) { Console.WriteLine(c); } Console.WriteLine("----------------------------"); Console.ReadKey(false); } } }
程序的運行結果如下:
7.Union用於合並兩個序列,並去掉重復項
8.Concat用於連接兩個序列,不會去掉重復項
9.Intersect用於獲得連個序列的交集
10.Except用於獲得兩個結合的差集
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Union_Concat_Intersect_Except { /// <summary> /// DebugLZQ /// http://www.cnblogs.com/DebugLZQ /// </summary> class Program { static void Main(string[] args) { string[] names1 = { "DebugLZQ", "Jerry", "Sarah", "Jerry", "Tom", "Linda", "M&M", "Jeffery" }; string[] names2 = { "DebugLZQ", "Jerry", "Sarah" }; //7.Union用於合並兩個序列,並去掉重復項 var names_Union = names1.Union(names2); //8.Concat用於連接兩個序列,不會去掉重復項 var names_Concat = names1.Concat(names2); //9.Intersect用於獲得連個序列的交集 var names_Intersect = names1.Intersect(names2); //10.Except用於獲得兩個結合的差集 var names_Except = names1.Except(names2); foreach (string name in names_Union) { Console.WriteLine(name); } Console.WriteLine("-----"); Console.ReadKey(false); foreach (string name in names_Concat) { Console.WriteLine(name); } Console.WriteLine("-----"); Console.ReadKey(false); foreach (string name in names_Intersect) { Console.WriteLine(name); } Console.WriteLine("-----"); Console.ReadKey(false); foreach (string name in names_Except) { Console.WriteLine(name); } Console.WriteLine("-----"); Console.ReadKey(false); } } }
程序的運行結果如下:
11.Range 用於生成指定范圍內的“整數”序列
12.Repeat用於生成指定數量的重復元素
13.Empty 用於獲得一個指定類型的空序列
14.DefaultIfEmpty 用於獲得序列,如果為空,則添加一個默認類型元素
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Range_Empty_DefalultIfEmpty { /// <summary> /// DebugLZQ /// http://www.cnblogs.com/DebugLZQ /// </summary> class Program { static void Main(string[] args) { //11.Range 用於生成指定范圍內的“整數”序列 var num2 = Enumerable.Range(10, 15); //12.Repeat用於生成指定數量的重復元素 var guest = new {Name="橙子",Age=25 }; var Guests = Enumerable.Repeat(guest, 5); //13.Empty 用於獲得一個指定類型的空序列 var empty = Enumerable.Empty<string>(); //14.DefaultIfEmpty 用於獲得序列,如果為空,則添加一個默認類型元素 //a var intempty = Enumerable.Empty<int>(); Console.WriteLine(intempty.Count()); Console.WriteLine("-----------"); foreach (var n in intempty) { Console.WriteLine(n); } Console.WriteLine("-----------"); Console.WriteLine(intempty.DefaultIfEmpty().Count()); Console.WriteLine("-----------"); foreach (var n in intempty.DefaultIfEmpty()) { Console.WriteLine(n); } Console.WriteLine("--------------------------"); Console.ReadKey(false); //b string[] names = { "DebugLZQ", "DebugMan", "Sarah", "Jerry", "Tom", "Linda", "M&M", "Jeffery" }; var query = from name in names where name == "LBJ" select name; Console.WriteLine(query.Count()); Console.WriteLine(query.DefaultIfEmpty().Count());//默認為null foreach (var n in query.DefaultIfEmpty()) { Console.WriteLine(n); } Console.WriteLine("---------------"); Console.ReadKey(false); //c指定一個默認值 foreach (var n in intempty.DefaultIfEmpty(100)) { Console.WriteLine(n); } Console.WriteLine("--------------------------"); Console.ReadKey(false); foreach (var n in query.DefaultIfEmpty("James")) { Console.WriteLine(n); } Console.ReadKey(false); } } }
程序的運行結果如下:
15.OfType篩選指定類型的元素
16.Cast類型轉換
17.AsEnumerable有些數據源類型不支持Enumerable的部分查詢關鍵字,需要轉換下,譬如IQueryable
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; namespace Cast_OfType_AsEnumerable { /// <summary> /// DebugLZQ /// http://www.cnblogs.com/DebugLZQ /// </summary> class Program { static void Main(string[] args) { ArrayList names = new ArrayList(); names.Add("DebugLZQ"); names.Add("Jerry"); names.Add(100); names.Add(new {Name="LZQ",Age=26}); names.Add(new Stack()); //15.OfType篩選指定類型的元素 var takenames = names.OfType<string>(); //16.Cast類型轉換 var takenames2 = names.OfType<string>().Cast<string>(); //17.AsEnumerable var takenames3 = takenames2.AsEnumerable(); foreach (var name in takenames3) { Console.Write("{0} ",name); } Console.ReadKey(false); } } }
程序運行結果如下:
延時執行,顧名思義就是不是立即執行,即不是在查詢語句定義的時候執行,而是在處理結果集(如遍歷)的時候執行,在Enumerable類方法成員中,除了本節總結的這常用的17個外,前面博文---LINQ基本子句 中總結的8個基本子句也都是延時執行的。注意延時執行的查詢程序的執行流程。