Linq做.Net開發的應該都用過,有些地方很復雜的邏輯用Linq很方便的解決。對於Linq to object、Linq to xml、Linq to sql、Linq to Entity(EF)都可以使用linq查詢。不知道大家有沒有想過為什么linq對這些都可以使用呢?統一的api適用這么多。其實主要還是IEnummerable<T>和IQueryable<T>兩個接口。可能有人會問為什么是兩個接口?這兩個接口有什么區別或者聯系呢?這又要引出來Enummerable、Queryable兩個類。
一、IEnummerable<T>和IQueryable<T>區別
using System.Runtime.InteropServices; namespace System.Collections { // // 摘要: // 公開枚舉數,該枚舉數支持在非泛型集合上進行簡單迭代。 [ComVisible(true)] [Guid("496B0ABE-CDEE-11d3-88E8-00902754C43A")] public interface IEnumerable { // // 摘要: // 返回一個循環訪問集合的枚舉器。 // // 返回結果: // 可用於循環訪問集合的 System.Collections.IEnumerator 對象。 [DispId(-4)] IEnumerator GetEnumerator(); } }
using System.Collections; using System.Linq.Expressions; namespace System.Linq { // // 摘要: // 提供對未指定數據類型的特定數據源的查詢進行計算的功能。 public interface IQueryable : IEnumerable { // // 摘要: // 獲取在執行與 System.Linq.IQueryable 的此實例關聯的表達式目錄樹時返回的元素的類型。 // // 返回結果: // 一個 System.Type,表示在執行與之關聯的表達式目錄樹時返回的元素的類型。 Type ElementType { get; } // // 摘要: // 獲取與 System.Linq.IQueryable 的實例關聯的表達式目錄樹。 // // 返回結果: // 與 System.Linq.IQueryable 的此實例關聯的 System.Linq.Expressions.Expression。 Expression Expression { get; } // // 摘要: // 獲取與此數據源關聯的查詢提供程序。 // // 返回結果: // 與此數據源關聯的 System.Linq.IQueryProvider。 IQueryProvider Provider { get; } } }
看它們兩個的定義也能看出IQueryable繼承IEnumerable,那兩者什么區別呢?那我們可以看下兩個接口的擴展類Enumerable、Queryable。
這兩個類API相同,但是仔細看可以發現參數類型不一樣,一個是Fuc<> 一個是Expression<Fuc<>>,其實Queryable是對Linq to sql、EF來使用的,雖然對外的api是一樣的,但實現的原理是不一樣的,一個是func<>lamdbs表達式,一個是Expression<Fuc<>>表達式樹。
二、Linq基礎查詢
前面區分了兩個接口,但API是一樣的,查詢用法也是一樣,只是提高數據源的方式不一樣。所以先拋開不一樣的,求同存異嘛,下面主要是講一下一樣的部分。
上面是在百科上下的圖片,雖然我也沒看明白這個圖,但在下面的幾篇博客中我會把Linq查詢的基本用法都列舉出來,今天這篇博客只是對Linq做一簡單介紹。
1.方法語法、查詢語法
書寫LINQ查詢時又兩種語法可供選擇:方法語法(Fluent Syntax)和查詢語法(Query Expression)。
LINQ方法語法是非常靈活和重要的,我們在這里將描述使用鏈接查詢運算符的方式來創建復雜的查詢,方法語法的本質是通過擴展方法和Lambda表達式來創建查詢。C# 3.0對於LINQ表達式還引入了聲明式的查詢語法,通過查詢語法寫出的查詢比較類似於SQL查詢。本篇會對LINQ方法語法進行詳細的介紹。
當然,.NET公共語言運行庫(CLR)並不具有查詢語法的概念。所以,編譯器會在程序編譯時把查詢表達式轉換為方法語法,即對擴展方法的調用。所以使用方法語法會讓我們更加接近和了解LINQ的實現和本質,並且一些查詢只能表示為方法調用,如檢索序列中的最大值、最小值元素的查詢,他們在查詢語法中就沒有對應的實現。但另一方面,查詢語法通常會比較簡單和易讀。不管怎樣,這兩種語法和互相補充和兼容的,我們可以在一個查詢中混合使用方法語法和查詢語法。
比如直接.List.where(p=>XX)這種是方法語法, from p in list XXXX這種是查詢語法,查詢語法還是轉換為方法語法。
2.延遲計算
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LinqDemo { class Program { static void Main(string[] args) { int[] a = new int[] { 1,3,5,7,9,11,2,4,6}; int b = 3; var result = from p in a where p % b == 0 select p; foreach (var m in result) { Console.WriteLine(m); } Console.WriteLine("-------------------------"); b = 2; foreach (var m in result) { Console.WriteLine(m); } Console.ReadLine(); } } }
上面代碼第一次是b=3,照數組a中3的倍數的數字,輸出是2、9、6,但當把b改為2時,再次輸出結果發現輸出的是2、4、6,這個例子主要說明Linq是延遲計算,和其他地方的懶加載是一樣的,聲明的時候並不會直接計算出來,而是等到用時在計算。