Language Integrated Query (LINQ) 是以將資料過濾、投影、排序、分組、查詢功能整合至 C# 語言的技術。傳統資料上列功能使用字串表示,不進行編譯時類型檢查,也不支援 IntelliSense。LINQ改變這種狀況,使上列功能成為語言的一部分,像類別、方法和事件一樣。

對於開發人員,LINQ 最明顯的「語言整合」部分是上列功能運算式,以宣告式「查詢語法」撰寫。這使得透過最少的程式碼,對資料來源進行過濾、投影、排序、分組。無論資料來源是 SQL 資料庫、ADO .NET 資料集、XML 文件還是 .NET 集合,基本查詢運算式模式都是相同的。

以下是 System.Linq 常見的類別和方法:
類別
  1. AggregateException:
  2. 這是一個類別,表示一個或多個例外狀況的集合。當在並行程式設計或其他情況下同時發生多個例外時,可以使用 AggregateException 來捕獲和處理這些例外。
  3. Enumerable:
  4. 這是一個靜態類別,包含了許多用於處理集合的擴充方法。其中的 GetEnumerator() 方法用於獲取集合的列舉器 (Enumerator),以便進行遍歷。
  5. EqualityComparer:
  6. 這是一個靜態類別,提供了相等比較的方法。它包含了一些用於比較兩個物件是否相等的靜態方法。
  7. Grouping:
  8. 這是一個類別,表示根據指定鍵分組的元素集合。當使用 GroupBy 方法對序列進行分組操作時,將返回一個包含 Grouping 物件的集合。
  9. Lookup:
  10. 這是一個類別,提供根據指定鍵查找元素的方法。與 Dictionary 不同,Lookup 可以包含重複的鍵,並且可以返回一個包含所有匹配鍵的序列。
方法
  1. All:
  2. 判斷序列中的所有元素是否都符合指定條件。
    bool allGreaterThanTen = numbers.All(x => x > 10);
  3. Any:
  4. 判斷序列中是否至少有一個元素符合指定條件。
    bool anyGreaterThanTen = numbers.Any(x => x > 10);
  5. AsEnumerable:
  6. 將物件轉換為 IEnumerable 介面。
    var enumerable = myObject.AsEnumerable();
  7. Average:
  8. 這是一個 LINQ 擴充方法,用於計算序列中元素的平均值。可以在數值序列上使用,例如整數或浮點數。
    List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
    double average = numbers.Average();
    Console.WriteLine("Average: " + average);
  9. Cast:
  10. 將序列中的元素轉換為指定的型別。
    var castedNumbers = mixedList.Cast<int>();
  11. Concat:
  12. 將兩個序列連接在一起。
    var concatenatedSequence = sequence1.Concat(sequence2);
  13. Contains:
  14. 判斷序列中是否包含指定元素。
    bool containsValue = sequence.Contains(42);
  15. Count:
  16. 獲取集合中的元素數量。
    int count = sequence.Count();
  17. DefaultIfEmpty:
  18. 如果序列為空,則返回指定的值;否則,返回序列中的第一個元素。
    var defaultValueIfEmpty = sequence.DefaultIfEmpty(defaultValue);
  19. Distinct:
  20. 返回序列中不重複的元素。
    var distinctValues = sequence.Distinct();
  21. ElementAt:
  22. 返回序列中位於指定索引處的元素。
    var element = sequence.ElementAt(5);
  23. ElementAtOrDefault:
  24. 返回序列中位於指定索引處的元素;如果索引超出範圍,則返回指定的值。
    var elementOrDefault = sequence.ElementAtOrDefault(5);
  25. Except:
  26. 返回序列中不包含在另一個序列中的元素。
    var exceptSequence = sequence1.Except(sequence2);
  27. First:
  28. 返回序列中的第一個元素。
    var firstElement = sequence.First();
  29. FirstOrDefault:
  30. 返回序列中的第一個元素;如果序列為空,則返回指定的值。
    var firstOrDefaultElement = sequence.FirstOrDefault();
  31. GroupBy:
  32. 根據指定鍵將序列中的元素分組。
    var groupedData = sequence.GroupBy(x => x.Category);
  33. Intersect:
  34. 返回兩個序列中都包含的元素。
    var intersectedValues = sequence1.Intersect(sequence2);
  35. Last:
  36. 返回序列中的最後一個元素。
    var lastElement = sequence.Last();
  37. LastOrDefault:
  38. 返回序列中的最後一個元素;如果序列為空,則返回指定的值。
    var lastOrDefaultElement = sequence.LastOrDefault();
  39. Max:
  40. 返回序列中的最大元素。
    var maxValue = sequence.Max();
  41. Min:
  42. 返回序列中的最小元素。
    var minValue = sequence.Min();
  43. OrderBy:
  44. 這是一個 LINQ 擴充方法,用於根據指定鍵對序列進行排序。可以使用它來對序列中的元素進行升序或降序排序。
    List<int> numbers = new List<int> { 5, 2, 8, 3, 1 };
    var orderedNumbers = numbers.OrderBy(num => num);
    foreach (var num in orderedNumbers)
    {
        Console.WriteLine(num);
    }
  45. OrderByDescending:
  46. 根據指定鍵對序列進行降序排序。
    var orderedDescendingSequence = sequence.OrderByDescending(x => x.Age);
  47. Reverse:
  48. 反轉序列中的元素順序。
    var reversedSequence = sequence.Reverse();
  49. Select:
  50. 這是一個 LINQ 擴充方法,用於將序列中的元素投影到新的序列中。可以使用它來對序列中的每個元素應用一個函數,並返回應用函數後的結果序列。
    List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
    var squaredNumbers = numbers.Select(num => num * num);
    foreach (var num in squaredNumbers)
    {
        Console.WriteLine(num);
    }
  51. Skip:
  52. 跳過序列中的指定數量的元素。
    var skippedSequence = sequence.Skip(5);
  53. Sum:
  54. 計算序列中元素的總和。
    var sum = sequence.Sum();
  55. Take:
  56. 從序列中擷取指定數量的元素。
    var takenSequence = sequence.Take(10);
  57. ToArray:
  58. 將序列轉換為陣列。
    var array = sequence.ToArray();
  59. ToList:
  60. 將序列轉換為列表。
    var list = sequence.ToList();
  61. ToLookup:
  62. 將序列轉換為 Lookup 物件。
    var lookup = sequence.ToLookup(x => x.Category);
  63. Where:
  64. 這是一個 LINQ 擴充方法,用於根據指定條件篩選序列中的元素。可以使用它來過濾出符合特定條件的元素,並返回滿足條件的元素序列。
    List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
    var evenNumbers = numbers.Where(num => num % 2 == 0);
    foreach (var num in evenNumbers)
    {
        Console.WriteLine(num);
    }
介面
  1. IOrderedEnumerable:
  2. 這是一個介面,表示一個已排序的元素序列。當使用 OrderBy 或 ThenBy 方法對序列進行排序時,將返回實現了 IOrderedEnumerable 介面的序列。

查詢運算式概觀
  1. 查詢運算式用於從任何已啟用 LINQ 的資料來源中查詢和轉換資料。例如,可從 SQL 資料庫擷取資料,並以 XML 資料流輸出。
  2. 查詢運算式易於掌握,使用許多熟悉的 C# 語言結構。
  3. 查詢運算式中的變數是強型別的,通常不需明確提供型別,編譯器能夠自行推斷。這提高了程式碼的簡潔性。
  4. 在逐一查看查詢變數之前(例如在 foreach 中),查詢不會執行。
  5. 查詢運算式在編譯期間會轉換成「標準查詢運算子」方法呼叫,雖然可使用方法語法表示相同的查詢,但查詢語法通常更容易閱讀。
  6. 建議優先使用查詢語法,只在必要時使用方法語法,因為兩者在語意和效能上沒有差異,而查詢語法通常更易讀。
  7. 某些查詢作業(例如 Count 或 Max)無對等的查詢運算式子句,需以方法呼叫表示。方法語法可與查詢語法結合。
  8. 查詢運算式可根據套用的型別編譯為運算式樹狀結構或委派,IEnumerable 編譯為委派,IQueryable 和 IQueryable 編譯為運算式樹狀結構。

格式說明
  • from:
  • 指定資料源,並設定迭代資料源內所有元素。
  • where:
  • 指定條件來過濾資料。
  • orderby:
  • 指定排序狀態,若無需排序則省略。
    ascending:升序。
    descending:降序。
  • select:
  • 指定要從查詢中選擇的元素。

常用範例
  1. 擷取項目子集,以產生新序列,而不需要修改個別項目。
  2. 然後,查詢可能會以各種方式排序或分組傳回的序列,如下列範例所示:

    範例:找出整數陣列內大於 80 的數字,按降序列出。  
    執行結果


  3. 擷取上述範例中的一系列項目,但將它們轉換為新類型的物件。
  4. 下面範例請注意 highScoresQuery2 的新類型。
    範例:找出整數陣列內大於 60 的數字,按升序列出。  
    執行結果


  5. 擷取來源資料的單一值。例如:最大值、最小值、總數量、符合條件第一個。
  6. 下面範例請注意 highScoresQuery2 的新類型。
    範例:找出整數陣列內大於 60 的數量。  
    執行結果


    範例:找出整數陣列內大於 60 的最大數字。  
    執行結果


    範例:找出整數陣低於 60 的第一個數字。  
    using static System.Formats.Asn1.AsnWriter;
    class Program
    {
        static void Main()
        {
            int[] scores = { 78, 91, 34, 82, 57, 24, 13, 18, 46 };
            int below60 = scores.FirstOrDefault(score => score < 60);;
            Console.WriteLine((below60!=0? $"低於 60 的第一個數字是: {below60}":
                                            "找不到低於 60 的數字。"));
        }
    }
    
    執行結果


    範例:將資料分成 3 組,偶數 一組,5 的倍數一組,剩下的一組。  
    using System;
    using System.Collections.Generic;
    using System.Linq;
    class Program
    {
        static void Main()
        {
            int[] scores = { 78, 95, 20, 37, 55, 28, 13, 17, 30, 19 };
            var groupedScores = scores
                .GroupBy(score => score % 2 == 0 ? "Even" : (score % 5 == 0 ? "MultipleOf5" : "Remaining"))
                .ToDictionary(group => group.Key, group => group.ToList());
    
            Console.WriteLine("Scores grouped by conditions:");
            //groupedScores.ForEach(group => Console.WriteLine($"Group: {group.Key}\n{string.Join(" ", group.Value)}"));
            foreach (var group in groupedScores)
            {
                Console.WriteLine($"Group: {group.Key}");
                Console.Write(string.Join(" ", group.Value));
            }
        }
    }
    
    執行結果


    範例:身分證檢查。 規則:
    第 1 個英文字母對應數字如下:
    ABCDEFGHIJKLM
    10111213141516173418192021

    NOPQRSTUVWXYZ
    22352324252627282932303133
    令 sum = 第 1 字母的拾位數 + 個位數乘以 9,有後數字乘上權重 8 往下遞減。
    以身分證號碼 S123456789 為例。
    S(26=>2⨯1+6⨯9=56) 1⨯8 2⨯7 3⨯6 4⨯5 5⨯4 6⨯3 7⨯2 8⨯1 9⨯1
    sum = 56 + 8 + 14 + 18 + 20 +20 + 18 + 14 + 8 + 9 = 185
    sum % 10 == 0 則正確,否則錯誤。

    程式碼:  
    using System;
    using System.Text.RegularExpressions;
    class Program
    {
        /// <summary>
        /// 查核身分證是否正確
        /// </summary>
        /// <param name="idNumber">身分證號碼</param>
        /// <returns>正確傳回 "正確" 否則 "格式錯誤或數值錯誤"</returns>
        static string ValidateIDNumber(string idNumber)
        {
            idNumber = idNumber.ToUpper();//轉大寫
            //使用正規化判斷格式
            if (!Regex.IsMatch(idNumber, @"^[A-Z]{1}[12][0-9]{8}$")) return "格式錯誤.";
            // 定義字母對應的數字
            int[] letterToNumber = new int[]
            {
                10, 11, 12, 13, 14, 15, 16, 17, 34, 18, 19, 20, 21,
                22, 35, 23, 24, 25, 26, 27, 28, 29, 32, 30, 31, 33
            };
            // 將字母轉換為對應的數字。(轉成大寫再找對應數字)
            var numbers = idNumber
                .Select((c, index) => index == 0 ? letterToNumber[c - 'A'] : int.Parse(c.ToString()))
                .ToArray();
            // 計算拾位數 + 個位數 * 8
            var sum = numbers[0] / 10 + (numbers[0] % 10) * 9 + numbers[9];
            // 計算總和
            sum += Enumerable.Range(1, 8)
                .Select(i => (10 - i - 1) * numbers[i])
                .Sum();
            // 驗證身分證
            return (sum % 10 == 0? "正確":"數值錯誤.");
        }
        static void Main()
        {
            string id;
            Console.WriteLine($"{id = "S123456789"} ==> {ValidateIDNumber(id)}");
            Console.WriteLine($"{id = "2223456789"} ==> {ValidateIDNumber(id)}");
            Console.WriteLine($"{id = "o199999992"} ==> {ValidateIDNumber(id)}");
            Console.WriteLine($"{id = "A034567890"} ==> {ValidateIDNumber(id)}");
            Console.WriteLine($"{id = "F24567890"} ==>  {ValidateIDNumber(id)}");
            Console.WriteLine($"{id = "B1456789011"} ==> {ValidateIDNumber(id)}");
            Console.WriteLine($"{id = "C156F89011"} ==> {ValidateIDNumber(id)}");
        }
    }
    
    執行結果


    範例:計算字串中有幾個大寫字母。
    int uppercaseCount = inputString.Count(char.IsUpper);
    範例:計算字串中有幾個小寫字母。
    int lowercaseCount = inputString.Count(char.IsLower);
    範例:計算字串中有幾個阿拉伯數字。
    int digitCount = inputString.Count(char.IsDigit);
    範例:找尋字串中最大整數。
    // 使用正規表達式匹配所有數字
    var matches = Regex.Matches(inputString, @"\d+");
    // 使用 LINQ 表達式找到最大整數
    int maxNumber = matches.Cast().Select(m => int.Parse(m.Value)).Max();