iT邦幫忙

2026 iThome 鐵人賽

DAY 18
0
Modern Web

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

Day 18|Lambda Expression:把判斷規則傳進Method

  • 分享至 

  • xImage
  •  

Day 16、Day 17 我們開始學習 Collection。

例如:

List<Order> orders =
    new List<Order>
    {
        new Order("A001", "TXF", 5),
        new Order("A002", "MXF", 20),
        new Order("A003", "TXF", 15)
    };

如果要找出:

Quantity > 10

目前可以:

foreach (Order order in orders)
{
    if (order.Quantity > 10)
    {
        Console.WriteLine(order.OrderId);
    }
}

但條件可能一直改變:

Quantity > 10
Product == "TXF"
Quantity == 5

真正改變的其實只有:

判斷條件

因此今天要學:

Lambda Expression

把「會改變的判斷規則」獨立出來。


1. 從一般 Method 到 Lambda

先看一般 Method:

bool IsLargeOrder(Order order)
{
    return order.Quantity > 10;
}

它可以理解成:

收到 Order
    ↓
判斷 Quantity > 10
    ↓
回傳 bool

相同的判斷邏輯,也可以使用 Lambda 表示:

order => order.Quantity > 10

同樣代表:

Order
 ↓
判斷
 ↓
bool

所以:

一般 Method
→ 有名稱的函式邏輯

Lambda
→ 通常是匿名的函式邏輯

2. Lambda 怎麼閱讀?

Lambda 常見的基本形式:

Parameter
    ↓
   =>
    ↓
Expression

例如:

order => order.Quantity > 10

拆開:

order
→ Parameter

=>
→ Lambda Operator

order.Quantity > 10
→ 要執行的判斷

可以讀成:

給我一個 order,判斷它的 Quantity 是否大於 10。

流程:

收到 Order
    ↓
Quantity > 10
    ↓
true / false

3. Lambda 和一般 Method 有什麼不同?

比較:

一般 Method

bool IsLargeOrder(Order order)
{
    return order.Quantity > 10;
}

Lambda

order => order.Quantity > 10

兩者可以表達相同的判斷,但使用方式不同。

Method Lambda
有明確名稱 通常匿名
適合重複使用 適合局部使用
適合較複雜邏輯 適合短小規則
名稱可以表達業務意義 可以直接看到當下條件

可以先這樣判斷:

短小、只使用一次
→ Lambda

重要、複雜、會重複使用
→ Method

但要注意:

Lambda 和 Method 都可以提供函式邏輯,不是互相排斥的兩種東西。


4. Func<Order, bool> 是什麼?

接下來會看到:

Func<Order, bool>

Func<...> 是 .NET 提供的一種 Delegate Type。

這篇不深入 Delegate 原理,可以先理解成:

Func 可以參考一段「有回傳值的函式邏輯」。

例如:

Func<Order, bool>
     ↑       ↑
   輸入     回傳

代表:

收到 Order
    ↓
執行某個判斷
    ↓
回傳 bool

所以:

order => order.Quantity > 10

符合:

Func<Order, bool>

因為它也是:

Order
 ↓
bool

5. Lambda 什麼時候才會執行?

例如:

Func<Order, bool> condition =
    order => order.Quantity > 10;

這時只是建立一段判斷邏輯。

並沒有立即判斷任何 Order。

真正執行是在:

condition(order);

假設:

order.Quantity = 20

流程:

condition(order)
      ↓
把 order 傳進 Lambda
      ↓
20 > 10
      ↓
true

所以:

建立 Lambda
→ 表示一段函式邏輯

呼叫 Lambda
→ 真正執行函式邏輯

Func 可以有多個 Parameter

Func 不一定只有一個輸入。

例如:

Func<int, int, int> add =
    (x, y) => x + y;

這裡:

Func<int, int, int>
     ↑    ↑    ↑
   輸入  輸入  回傳

代表:

收到 int
收到 int
    ↓
回傳 int

所以:

(x, y) => x + y

可以讀成:

給我 x 和 y,回傳兩者相加的結果。

例如:

int result =
    add(3, 5);

Console.WriteLine(result);

結果:

8

初學可以先記:

Func 最後一個 Type 是回傳型別,前面的 Type 都是輸入。

例如:

Func<int, bool>

int
 ↓
bool

以及:

Func<int, int, int>

int
int
 ↓
int

沒有回傳值可以使用 Action

如果一段函式邏輯:

只需要執行動作
不需要回傳結果

可以使用:

Action<string> sayHello =
    name =>
        Console.WriteLine($"Hello, {name}");

使用:

sayHello("Amy");

結果:

Hello, Amy

這裡:

Action<string>

可以理解成:

收到 string
    ↓
執行動作
    ↓
沒有回傳值

所以初學可以這樣區分:

Type 用途
Func 有回傳值
Action 沒有回傳值

例如:

Func<Order, bool>
→ Order → bool

而:

Action<string>
→ 收到 string
→ 執行動作
→ 不回傳資料

這篇後面的主線仍然以:

Func<Order, bool>

為主。


Expression Lambda 與 Statement Lambda

目前看到的:

order => order.Quantity > 10

右邊只有一個 Expression。

這種寫法叫:

Expression Lambda

例如:

Func<int, int> doubleNumber =
    number => number * 2;

如果 Lambda 需要執行多個步驟,可以使用 { }:

Func<int, int> doubleNumber =
    number =>
    {
        int result =
            number * 2;

        Console.WriteLine(result);

        return result;
    };

這種寫法叫:

Statement Lambda

整理:

Expression Lambda
→ 一個 Expression
→ 寫法簡潔
Statement Lambda
→ 使用 { }
→ 可以有多個 Statement
→ 有回傳值時要使用 return

例如:

number => number * 2

適合簡單邏輯。

如果邏輯開始需要:

建立變數
判斷
輸出
多個步驟

就可以使用 Statement Lambda。

但如果邏輯越來越複雜,也應該考慮改成有名稱的 Method。


6. 把判斷規則傳進 Method

現在回到最一開始的問題。

原本:

foreach (Order order in orders)
{
    if (order.Quantity > 10)
    {
        // 處理
    }
}

真正會改變的是:

order.Quantity > 10

因此我們建立:

List<Order> FindOrders(
    List<Order> orders,
    Func<Order, bool> condition)
{
    List<Order> result =
        new List<Order>();

    foreach (Order order in orders)
    {
        if (condition(order))
        {
            result.Add(order);
        }
    }

    return result;
}

這個 Method 有兩個重要 Parameter。

List<Order> orders

代表:

要處理的資料

而:

Func<Order, bool> condition

代表:

要使用的判斷規則

所以:

FindOrders()

資料
+
判斷規則

7. condition(order) 到底是什麼?

這是今天最重要的一行:

if (condition(order))

假設呼叫端傳入:

order => order.Quantity > 10

那麼 condition 參考的就是這段函式邏輯。

因此:

condition(order)

可以理解成:

把目前這筆 order 傳進判斷規則,並取得 true 或 false。

例如:

A001
Quantity = 5

執行:

condition(A001)
    ↓
5 > 10
    ↓
false

所以不加入 result。

而:

A002
Quantity = 20

執行:

condition(A002)
    ↓
20 > 10
    ↓
true

所以加入 result。

核心流程:

foreach 取得 Order
        ↓
condition(order)
        ↓
執行判斷規則
        ↓
true
→ 加入 result

false
→ 不加入

8. 換 Lambda,就能換篩選條件

找:

Quantity > 10
List<Order> result =
    FindOrders(
        orders,
        order =>
            order.Quantity > 10
    );

如果改成:

Product == "TXF"

只需要換掉 Lambda:

List<Order> result =
    FindOrders(
        orders,
        order =>
            order.Product == "TXF"
    );

如果再改成:

Quantity == 5
List<Order> result =
    FindOrders(
        orders,
        order =>
            order.Quantity == 5
    );

可以看到:

FindOrders()
→ 固定流程不變

Lambda
→ 判斷規則改變

這就是:

把固定流程與可變規則分開。


9. 也可以傳一般 Method

假設:

bool IsLargeOrder(Order order)
{
    return order.Quantity > 10;
}

它也是:

Order
 ↓
bool

符合:

Func<Order, bool>

所以也可以:

List<Order> result =
    FindOrders(
        orders,
        IsLargeOrder
    );

因此以下兩種都可以。

使用 Lambda:

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

使用 Method:

FindOrders(
    orders,
    IsLargeOrder
);

可以這樣理解:

              函式邏輯
                  │
          ┌───────┴───────┐
          ↓               ↓
       Lambda           Method
          │               │
          └───────┬───────┘
                  ↓
       都可以符合
       Func<Order, bool>

10. Lambda 和 Method 怎麼選?

如果條件:

很短
只使用一次
直接寫在呼叫位置很好懂

適合:

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

如果規則:

會重複使用
具有明確業務意義
邏輯比較複雜

適合建立 Method:

bool IsLargeOrder(Order order)
{
    return order.Quantity > 10;
}

再:

FindOrders(
    orders,
    IsLargeOrder
);

所以:

短、小、局部
→ Lambda

重要、複雜、重複使用
→ Method

11. 完整範例

List<Order> orders =
    new List<Order>
    {
        new Order(
            "A001",
            "TXF",
            5
        ),

        new Order(
            "A002",
            "MXF",
            20
        ),

        new Order(
            "A003",
            "TXF",
            15
        )
    };

List<Order> largeOrders =
    FindOrders(
        orders,
        order =>
            order.Quantity > 10
    );

foreach (Order order in largeOrders)
{
    Console.WriteLine(
        order.OrderId
    );
}

List<Order> FindOrders(
    List<Order> source,
    Func<Order, bool> condition)
{
    List<Order> result =
        new List<Order>();

    foreach (Order order in source)
    {
        if (condition(order))
        {
            result.Add(order);
        }
    }

    return result;
}

class Order
{
    public string OrderId
    {
        get;
    }

    public string Product
    {
        get;
    }

    public int Quantity
    {
        get;
    }

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

結果:

A002
A003

整體只需要掌握:

List<Order>
      ↓
FindOrders()
      ↓
接收判斷規則
      ↓
condition(order)
      ↓
執行規則
      ↓
符合條件
→ 加入 result

Day 18 小結

今天最重要的是:

Method
→ 有名稱的函式邏輯
Lambda
→ 通常是匿名函式邏輯

而:

Func<Order, bool>

代表:

需要一段:

Order
 ↓
bool

的函式邏輯

所以可以提供:

Lambda
或
Method

而:

condition(order)

代表:

把 order 傳進函式邏輯
        ↓
真正執行
        ↓
取得結果

另外今天也認識:

Func<T1, T2, TResult>
→ 可以有多個輸入

Action<T>
→ 執行動作,不回傳結果

Expression Lambda
→ 單一 Expression

Statement Lambda
→ 使用 { } 撰寫多個 Statement

一句話記住:

Lambda 適合直接表示與傳遞短小的函式規則;有名稱的 Method 更適合重要、複雜或會重複使用的邏輯。


下一篇|Day 19:LINQ

今天我們自己建立:

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

內部需要:

foreach
↓
condition(order)
↓
加入符合條件的資料

下一篇會看到 LINQ 已經提供:

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

因此 Day 19 會開始學:

Where()
→ 篩選

Select()
→ 轉換

FirstOrDefault()
→ 找第一筆

Any()
→ 判斷是否存在

從:

自己寫 FindOrders()

走向:

使用 LINQ 查詢 Collection

上一篇
Day 17|Dictionary 與 HashSet:用 Key 找資料、避免資料重複
下一篇
Day 19|LINQ 基礎:用一致的方式查詢 Collection
系列文
現在就學C# 與 ASP.NET Core 共 19 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言