iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0
Modern Web

現在就學C# 與 ASP.NET Core系列 第 19 篇

Day 19|LINQ 基礎:用一致的方式查詢 Collection

  • 分享至 

  • xImage
  •  

Day 18 我們自己建立了 FindOrders():

FindOrders(
    orders,
    order => order.Quantity > 10
);

由 FindOrders() 負責逐筆處理資料,再由 Lambda 決定篩選條件。

但實際開發中,以下需求非常常見:

篩選資料
轉換資料
排序資料
判斷資料是否存在
取得第一筆符合條件的資料

如果每次都重新撰寫 foreach 與處理流程,很容易產生重複程式。

因此 C# 提供了:

LINQ

例如 Day 18 的:

FindOrders(
    orders,
    order => order.Quantity > 10
);

使用 LINQ 可以改成:

orders.Where(
    order => order.Quantity > 10
);

今天就從這裡正式進入 LINQ。


今天會學到

Where()
→ 篩選

Select()
→ 轉換

Any()
→ 判斷是否存在

FirstOrDefault()
→ 取得第一筆

OrderBy()
→ 排序

ToList()
→ 建立 List

今天最重要的觀念:

LINQ 負責資料操作,Lambda 負責操作規則。


1. LINQ 是什麼?

LINQ 全名:

Language Integrated Query

中文通常稱為:

語言整合查詢

初學可以先理解成:

LINQ 是 C# 提供的一套資料查詢與處理方式。

例如:

orders.Where(
    order => order.Quantity > 10
);

這段程式可以拆成兩個角色:

Where()
→ LINQ Method
→ 負責篩選
order => order.Quantity > 10

則是 Day 18 學過的 Lambda:

Lambda
→ 提供篩選規則

所以可以這樣理解:

LINQ
→ 要對資料做什麼?

Lambda
→ 要使用什麼規則?

2. 範例資料

今天都使用同一組 Order:

using System.Linq;

List<Order> orders =
    new List<Order>
    {
        new Order("A001", "TXF", 5, 1500m),
        new Order("A002", "MXF", 20, 800m),
        new Order("A003", "TXF", 15, 1600m),
        new Order("A004", "EXF", 8, 1200m),
        new Order("A005", "TXF", 25, 1700m)
    };

class Order
{
    public string OrderId { get; }

    public string Product { get; }

    public int Quantity { get; }

    public decimal Price { get; }

    public Order(
        string orderId,
        string product,
        int quantity,
        decimal price)
    {
        OrderId = orderId;
        Product = product;
        Quantity = quantity;
        Price = price;
    }
}

資料如下:

OrderId Product Quantity Price
A001 TXF 5 1500
A002 MXF 20 800
A003 TXF 15 1600
A004 EXF 8 1200
A005 TXF 25 1700

3. Where():篩選資料

需求:

找出 Quantity 大於 10 的 Order。

可以使用:

List<Order> result =
    orders
        .Where(
            order => order.Quantity > 10
        )
        .ToList();

Where() 的用途是:

逐筆判斷資料,只保留條件為 true 的元素。

這裡的:

order => order.Quantity > 10

代表:

收到 Order
    ↓
判斷 Quantity > 10
    ↓
true / false

實際結果:

Order 判斷 結果
A001 / 5 5 > 10 排除
A002 / 20 20 > 10 保留
A003 / 15 15 > 10 保留
A004 / 8 8 > 10 排除
A005 / 25 25 > 10 保留

最後取得:

A002
A003
A005

如果條件改成:

Product == "TXF"

只需要更換 Lambda:

List<Order> txfOrders =
    orders
        .Where(
            order => order.Product == "TXF"
        )
        .ToList();

所以:

Where()
→ 負責篩選

Lambda
→ 負責篩選條件

另外,Where() 不會把不符合條件的資料從原本的 orders 刪掉,而是產生符合條件的查詢結果。


4. Select():轉換資料

如果需求不是「哪些資料要留下」,而是:

只取得每筆 Order 的 Product。

這時使用:

Select()

例如:

List<string> products =
    orders
        .Select(
            order => order.Product
        )
        .ToList();

結果:

TXF
MXF
TXF
EXF
TXF

這裡:

order => order.Product

代表:

收到 Order
    ↓
取得 Product
    ↓
回傳 string

所以資料從:

Order

轉換成:

string

Select() 的核心就是:

把每一筆來源資料轉換成需要的結果。


5. Where() 和 Select() 的差別

這兩個是 LINQ 最重要的基礎 Method。

orders.Where(
    order => order.Quantity > 10
);

Where() 的 Lambda 回傳 bool,是在判斷:

這筆資料要不要留下?

而:

orders.Select(
    order => order.Product
);

Select() 則是在決定:

這筆資料要變成什麼?

整理:

Method 核心問題 用途
Where() 要不要這筆? 篩選
Select() 要變成什麼? 轉換

一句話記住:

Where() 決定哪些資料留下;Select() 決定資料要變成什麼。


6. Any():有沒有符合條件的資料?

有時候不需要取得所有符合條件的資料,只想知道:

有沒有 Quantity 大於 10 的 Order?

可以:

bool hasLargeOrder =
    orders.Any(
        order => order.Quantity > 10
    );

結果:

true

如果:

bool hasLargeOrder =
    orders.Any(
        order => order.Quantity > 100
    );

結果:

false

所以:

Any()
→ 只回答「有沒有?」

有符合資料
→ true

沒有
→ false

和 Where() 的差別:

Where()
→ 我要符合條件的資料

Any()
→ 我只想知道有沒有

7. FirstOrDefault():取得第一筆

需求:

找出第一筆 Product 是 TXF 的 Order。

可以:

Order? order =
    orders.FirstOrDefault(
        order => order.Product == "TXF"
    );

結果是:

A001

如果找不到:

Order? order =
    orders.FirstOrDefault(
        order => order.Product == "ABC"
    );

在這個 Order 範例中會得到:

null

因此可以:

if (order is not null)
{
    Console.WriteLine(order.OrderId);
}
else
{
    Console.WriteLine("找不到訂單");
}

先記住:

FirstOrDefault()
→ 第一筆符合條件的資料

找到
→ 回傳那筆資料

找不到
→ 回傳 Default Value

目前 Order 是 Reference Type,因此這裡找不到時是 null。


8. OrderBy():排序資料

需求:

依 Quantity 從小到大排序。

可以:

List<Order> sortedOrders =
    orders
        .OrderBy(
            order => order.Quantity
        )
        .ToList();

結果:

A001 → 5
A004 → 8
A003 → 15
A002 → 20
A005 → 25

這裡:

OrderBy()
→ 執行排序

order.Quantity
→ 指定排序依據

如果要由大到小:

List<Order> sortedOrders =
    orders
        .OrderByDescending(
            order => order.Quantity
        )
        .ToList();

所以:

OrderBy()
→ 小到大

OrderByDescending()
→ 大到小

9. LINQ 可以串接使用

前面都是單獨使用 LINQ Method。

實際開發中,更常見的是把多個操作組合起來。

需求:

找出 Product 是 TXF,而且 Quantity 大於 10 的 Order,依 Quantity 由大到小排序,最後只取得 OrderId。

可以:

List<string> result =
    orders
        .Where(
            order => order.Product == "TXF"
        )
        .Where(
            order => order.Quantity > 10
        )
        .OrderByDescending(
            order => order.Quantity
        )
        .Select(
            order => order.OrderId
        )
        .ToList();

從上往下閱讀:

orders
   ↓
Where()
只保留 Product == "TXF"
   ↓
Where()
只保留 Quantity > 10
   ↓
OrderByDescending()
依 Quantity 由大到小
   ↓
Select()
轉成 OrderId
   ↓
ToList()
建立 List<string>

最後結果:

A005
A003

這種將多個 Method 串在一起的寫法稱為:

Method Chaining

也就是:

把多個資料處理操作組合成一個查詢流程。

這也是 LINQ 很重要的使用方式。


10. ToList():建立 List

前面的範例常看到:

.ToList();

例如:

List<Order> result =
    orders
        .Where(
            order => order.Quantity > 10
        )
        .ToList();

初學可以先理解:

如果最後需要一個 List<T>,可以使用 ToList() 將查詢結果建立成 List。

例如:

Where(...)
↓
查詢結果
↓
ToList()
↓
List<Order>

不是所有 LINQ Method 都需要 ToList()。

例如:

bool exists =
    orders.Any(
        order => order.Quantity > 10
    );

Any() 已經直接得到:

bool

而:

Order? order =
    orders.FirstOrDefault(
        order => order.Product == "TXF"
    );

FirstOrDefault() 已經直接取得一筆資料或 null。

補充:Where()、Select() 等部分 LINQ 查詢具有 Deferred Execution(延遲執行)的特性


11. 怎麼選擇 LINQ Method?

學 LINQ 不需要一開始背很多 API。

先問自己:

我現在想對資料做什麼?

需求 LINQ Method
哪些資料符合? Where()
資料要變成什麼? Select()
有沒有符合的資料? Any()
第一筆符合的是誰? FirstOrDefault()
怎麼排序? OrderBy() / OrderByDescending()
最後需要 List? ToList()

可以簡單記成:

哪些?
→ Where()

變什麼?
→ Select()

有沒有?
→ Any()

第一筆?
→ FirstOrDefault()

怎麼排?
→ OrderBy()

要 List?
→ ToList()

重點不是背 Method Name。

而是:

先理解需求,再選擇適合的 LINQ Method。


Day 19 小結


今天從 Day 18 自己建立:

FindOrders(
    orders,
    order => order.Quantity > 10
);

進一步走到 LINQ:

orders.Where(
    order => order.Quantity > 10
);

核心關係是:

LINQ
→ 負責資料操作

Lambda
→ 負責操作規則

常用 Method:

Where()
→ 篩選

Select()
→ 轉換

Any()
→ 有沒有

FirstOrDefault()
→ 第一筆

OrderBy()
→ 排序

ToList()
→ 建立 List

最後記住兩件事:

先理解需求,再選擇 LINQ Method。

LINQ 負責「對資料做什麼」,Lambda 負責「使用什麼規則」。


上一篇
Day 18|Lambda Expression:把判斷規則傳進Method
系列文
現在就學C# 與 ASP.NET Core 共 19 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言