什麼是linq
語言整合查詢 (LINQ) 是一組基於將查詢功能直接整合到 C# 語言中的技術的名稱。傳統上,對資料的查詢表示為簡單的字串,在編譯時沒有型別檢查或 IntelliSense 支援。此外,您必須為每種型別的資料來源學習不同的查詢語言:SQL 資料庫、XML 文件、各種 Web 服務等等。使用 LINQ,查詢是一流的語言結構,就像類、方法、事件一樣。您可以使用語言關鍵字和熟悉的運算子針對強型別的物件集合編寫查詢。LINQ 系列技術為物件 (LINQ to Objects)、關聯式資料庫 (LINQ to SQL) 和 XML (LINQ to XML) 提供一致的查詢體驗。
之前我們查詢集合中的資料一般會使用for或foreach語句來進行查詢,Linq 使用查詢表示式來進行查詢,Linq 表示式比之前用for或forach的方式更加簡潔,比較容易新增篩選條件。
將下列int集合整體每個元素擴大10倍
var num = new List<int>() { 1,2,3,4,5}; IEnumerable<int> query = num.Select(n => n * 10); foreach (int n in query) Console.WriteLine(n);
從上面的例子可以看出,linq集合在查詢是簡單了很多,並且很容易新增篩選條件。
linq原理
編譯器是如何處理這些查詢表示式的呢,為了理解好這個問題就要先解釋一下linq的底層思想。
1.序列
序列是linq的基礎。序列是通過過IEnumerable和IEnumerable<T>介面進行封裝,如果某個型別實現了IEnumerable介面,就意味著它可以被迭代訪問。序列就像一個資料的傳送帶,每次只能獲取一個,知道你不想傳了或者序列中沒有資料了。序列和其他集合資料結構(比如列表和陣列)之間最大的區別就是,當你從序列讀取資料的時候,通常不知道還有多少資料項等待讀取,或者不能訪問任意的資料項——只能是當前的這個。在你看到一個linq查詢表示式的時候,應該要想到它所涉及的序列:一開始總是存在至少一個序列,且通常在中間過程會轉換為其他序列,也可能和更多的序列連線在一起。
舉個例子,獲取成年人姓名的表示式
var names = from person in people where person.Age >= 18 select person.Name;
foreach (var n in names)
Console.WriteLine(n);
然後講這個表示式分解成獨立的步驟:每一個箭頭代表一個序列。每個框都代表查詢表示式的一個步驟。我們獲取他整個家庭成員(用Person物件表示)。接著經過過濾後,序列就只包含成人了(還是用Person物件表示)。而最終的結果以字串形式包含這些成人的名字。每個步驟就是得到一個序列,在序列上應用操作以生成新的序列。結果不是字串"Holly"和"Jon"——而是IEnumerable <String>,這樣,在從裡面一個接一個獲取元素的時候,將首先生成"Holly",其次得到"Jon"。
查詢操作並不是在查詢運算子定義的時候執行,而是在真正使用集合中的資料時才執行(如:在遍歷集合時呼叫MoveNext方法和Current檢查),再舉個簡單的例子
var num = new List<int>(); num.Add(1); IEnumerable<int> query = num.Select(n => n * 10); foreach (int n in query) Console.WriteLine(n); num.Add(2); foreach (int n in query) Console.WriteLine(n);
輸出的結果是10 20
絕大部分標準的LINQ查詢運算子都具有延遲載入這種特性,但也有例外:
- 那些返回單個元素或返回一個數值的運算子,如First或Count。
- 轉換運算子:ToArray,ToList,ToDictonnary,ToLookup。
以上這些運算子都會觸發LINQ語句立即執行,因為它們的返回值型別不支援延遲載入。
3.標準查詢操作符
var list = query.Where(m => m.PID == corpID && m.type == 8);
2.OrderBy
var list = query.Where(m => m.PID == corpID && m.type == 8).OrderBy(m => m.PID);
3.join連線
var q2 = from u in dataContext.useinfo join d in dataContext.useDetails on u.id equals d.id select u; //join時必須將join後的表into到一個新的變數XX中,然後要用XX.DefaultIfEmpty()表示外連線。 //DefaultIfEmpty使用了泛型中的default關鍵字。default關鍵字對於引用型別將返回null,而對於值型別則返回0。 //對於結構體型別,則會根據其成員型別將它們相應地初始化為null(引用型別)或0(值型別) var q3 = from u in dataContext.useinfo join d in dataContext.useDetails on u.id equals d.id into f from c in f.DefaultIfEmpty() select c;
附件:linq原始碼 https://github.com/dotnet/runtime
【參考】《深入理解C#》