Closure
Closure,就像人生的記憶機制。它讓一段邏輯,不只記得「要做什麼」,也記得「曾經與誰有關」。在我們離開某個場景之後,它仍攜帶著當時的狀態、情緒與環境,就像一個攜帶回憶的函式,延續著那份未完的故事。Closure 使「lambda 可以用外部變數」,但它的本質遠不只是「抓外部變數來用」,而是一種讓函式擁有「記憶狀態」的機制


立刻執行 → 當下的值是什麼就拿什麼
1 | void Show(int n) |
而 Lambda 並不是幫你把當下的值存起來,而是把「之後要用哪一個變數」記住,所以多個 Lambda 如果指向同一個變數,就一定會互相干擾,Lambda 的目的是「延後執行一段邏輯」,而不是「立即複製資料」
1 | var actions = new List<Action>(); |
這裡做的是宣告一個變數 i,而整個 for 迴圈裡的 i 只有「一個」,每次 Add 的時候 Lambda 並沒有執行,只是記下「未來要用 這個 i 來印」,所以 List 裡其實放的是 5 個「指向同一個 i 的行為」,不是 5 個不同的值,當 for 迴圈結束,i 已經變成 5,之後當開始 Invoke 會發現每個 Lambda 都去看同一個 i,所以全部印 5,關鍵是「它們用的是同一個變數來源」
Lambda 表達式捕獲的是變數
i的參考,而不是值的副本。當 Lambda 實際執行時,i的值已經是迴圈結束後的 5


✅ 解決方案
幫每個 Lambda 配一個「專屬變數」,讓它們不要共用同一個狀態
1 | var actions = new List<Action>(); |
每一次 loop 都建立一個新的區域變數 copy,每一個 Lambda 都只認得自己那顆 copy,所以狀態被「隔離」,不會再互相干預,這是一種刻意切斷共享狀態的寫法


1 | // 原始程式碼 |

1 | public static Action CreateClickCounter() |
return () => { ... } 捕捉了 count,所以 count 被放進編譯器隱藏生成的物件裡
編譯器轉換過程
1 | Func<int> CreateCounter() |
編譯器轉換版本
1 | class Closure |
count原本是區域變數 → 被提升為Closure類別的欄位- lambda 不再只是一個方法,而是「帶著記憶體(狀態)的 delegate」
- 即使
CreateCounter()執行完畢,closure 物件還活著(因為 delegate 持有參考)
1 | var counter = CreateClickCounter(); |

1 | public static Action CreateEventCounter(string eventName) |

1 | public static Func<int, int> CreateAccumulator(int initialValue = 0) |

1 | public static Action<string> CreateStateMachine() |

1 | var promotionTagIds = GetPromotionTagIds(); |
這裡的 lambda () => ... 就是個 Closure,它沒有參數,但它裡面用了「外部的變數」
promotionTagIdsouterIdstargetTypeEnum



