C# LINQ 從 0 到 1 基礎教學


什麼是 LINQ?

LINQ 全名是 Language Integrated Query (語言綜合查詢),念作 link (另可),是一個用來查詢包含 集合、SQL、XML、JSON(第三方套件) 等資料的語言,只是可以使用 C#, VB 來使用而已。並且不只是查詢,還包含資料轉換、合併、排序、篩選(過濾)等等,幫助我們快速的處理資料。

LINQ 的寫法種類

LINQ 有兩種寫法:方法語法和查詢語法,下面兩個程式碼是等效的:

方法語法(Method syntax)

又稱為流利語法(Fluent Syntax),
    
int[] numbers = { 5, 10, 8, 3, 6, 12 };

IEnumerable<int> numQuery = numbers
    .Where(num => num % 2 == 0) // 找出偶數
    .OrderBy(n => n) // 由小到大排序
    .Select(n => n); // 取出所有 num 的值 // 本行可省略
    
Console.WriteLine(string.Join(", ", numQuery));
// 6, 8, 10, 12
    

查詢語法(Query syntax)

查詢語法不是標準的 C# 語法, .NET 的執行環境(Common Language Runtime)並沒有辦法直接看懂查詢語法,編譯器會將之轉換為方法語法再執行
    
int[] numbers = { 5, 10, 8, 3, 6, 12 };

IEnumerable<int> numQuery =
    from num in numbers // 將 numbers 陣列中的值取出,逐一歷遍,變數名稱為 num
    where num % 2 == 0 // 找出偶數
    orderby num // 由小到大排序
    select num; // 取出所有 num 的值

Console.WriteLine(string.Join(", ", numQuery));
// 6, 8, 10, 12
    

筆者比較習慣使用方法語法,筆者自己還是新手時也覺得方法語法比較好理解,並且是標準的 C# 語法,不用再學習「另一種規則」。以下的說明都會使用方法語法作示範

註: 本篇文章使用 .NET 6 (C# 10) 的語法做示範,不同版本間的使用可能會有所差異。

LINQ 基礎

終於要開始寫 LINQ 了,上面有提到集合可以使用 LINQ ,具體就是有實作 IEnumerable 或 IQueryable 的 GetEnumerator 就可以使用 LINQ ,例如 List, Array, Dictionary, IQueryable, IEnumerable, HashSet 等等都可以使用 LINQ

使用 Where 過濾

使用 Where 進行過濾, Where 中需要指定一個參數,用來代表歷遍這個集合的當前元素,回傳出來的結果會是 IEnumerable 型態,例如下面使用 Where 過濾找出所有內容大於 5 的元素:
    
List<int> list = new() { 5, 10, 8, 3, 6, 12 };
IEnumerable<int> ints = list.Where(x=>x > 5);

foreach (var i in ints)
{
    Console.WriteLine(i);
}

/*
10
8
6
12
*/
    

變更回傳型態,不要是 IEnumerable

LINQ 的回傳值通常都是 IEnumerable ,它可以很方便的歷遍,對於之後要把內容「處理一次」的話非常建議直接使用,可是如果把 IEnumerable「處理多次」就會出現以下提示:
    
Possible multiple enumeration
    

我們可以把 IEnumerable 轉換為 List ,好處是會產生索引,要取得指定位置的元素會比較快,不用從頭尋找。並且才可以增加/修改內容等等,不過轉換的壞處就是會需要直接分配記憶體空間,對於較大的內容會讓速度變慢、占用資源。

除了 List 外,還可以轉換為 Array, HashSet 和 Dictionary,只是 Dictionary 稍稍有點複雜,下次再討論:
    
IEnumerable<int> ints = list.Where(x=>x > 5);
IEnumerable<int> ints = list.Where(x=>x > 5);

int[] array = ints.ToArray();
List<int> list1 = ints.ToList();
HashSet<int> hashSet = ints.ToHashSet();
    

去重複

集合內容有重複的資料怎麼辦?可以使用 Distinct 直接去重複,一行解決:
    
List<int> list = new() { 1, 2, 3, 2, 1 };
IEnumerable<int> enumerable = list.Distinct();
Console.WriteLine(string.Join(", ", enumerable)); // 將清單使用 ", " 符號串接
// 1, 2, 3
    

其實在 C# 中去重複還有另一個小技巧,HashSet 是不保證順序的不會重複的集合,所以也可以一行解決重複的問題:
    
List<int> list = new() { 1, 2, 3, 2, 1 };
HashSet<int> hashSet = list.ToHashSet();
Console.WriteLine(string.Join(", ", hashSet)); // 將清單使用 ", " 符號串接
// 1, 2, 3
    

取得總數

Count() 就是取得集合的總個數,看了一下 namespace 確實是歸類在 System.Linq
    
List<int> list = new() { 1, 2, 3, 2, 1 };
int count = list.Count();
Console.WriteLine($"Count: {count}");
// Count: 5
    



內容太少了嗎?那可以看看筆者之前寫的其他 LINQ 教學,依照順序把這些都學會應該就沒有問題可以難的倒你了吧?如果有的話下面留言,我會繼續寫下去!

延伸閱讀:
C# LINQ 基礎: 取得內容 大全(First, Last, Take, Skip, Single, Max, Min)
C# LINQ Select 和 SelectMany 方法介紹
C# LINQ 排序介紹 (OrderBy, ThenBy)
C# 使用 GroupBy 將資料分組 示範
C# 使用 LINQ 操作集合大全(合併、聯集、差集、交集、合併計算)
C# join 指令使用介紹 (GroupJoin)(Left Join, Full Join)
C# Enumerable 的 OfType 和 Cast 介紹
C# 多欄位動態排序示範

參考資料:
Wiki - Language Integrated Query
Wiki - 語言整合查詢
Microsoft.Learn - Write LINQ queries in C#
Microsoft.Learn - Common Language Runtime (CLR) overview
Microsoft.Learn - Enumerable.Where Method

留言