Lambda 表達式中捕獲的變數:揭開「foreach 循環變數」之謎
考慮下面令人困惑的程式碼,其目的是創建一個清單迎接各種資料類型的方法:
public class MyClass { public delegate string PrintHelloType(string greeting); public void Execute() { Type[] types = new Type[]{typeof(string), typeof(float), typeof(int)}; List<PrintHelloType> helloMethods = new List<PrintHelloType>(); foreach (var type in types) { var sayHello = new PrintHelloType(greeting => SayGreetingToType(type, greeting)); helloMethods.Add(sayHello); } foreach (var helloMethod in helloMethods) { Console.WriteLine(helloMethod("Hi")); } } public string SayGreetingToType(Type type, string greetingText) { return greetingText + " " + type.Name; } }
但是,當您運行此程式碼時,您會遇到意想不到的結果:它列印了三次“Hi Int32”,而不是預期的「Hi String」、「Hi單」、「嗨Int32」。這是怎麼回事?
閉包和捕獲變數的微妙之處
理解這種行為的關鍵在於閉包和捕獲變數的概念。建立 lambda 表達式時,它會從其周圍範圍捕獲變數。在這種情況下,lambda 表達式會從 foreach 迴圈中擷取類型變數。
捕獲的循環變數:一個陷阱
至關重要的是,捕獲的變數不是類型,而是變數本身。隨著循環的進行,類型變數發生變化,而 lambda 表達式仍然引用相同的捕獲變數。
修正問題
為了實現預期的行為,它有必要在循環中建立一個新變數來捕捉預期值。以下是修改程式碼的方法:
foreach (var type in types) { var newType = type; var sayHello = new PrintHelloType(greeting => SayGreetingToType(newType, greeting)); helloMethods.Add(sayHello); }
透過擷取 newType 而不是 type,lambda 表達式現在擁有對預期值的常數引用,並獲得預期的輸出。
結論
使用 lambda 表達式時,了解閉包和捕獲變數的複雜性至關重要。留意這些機制,您可以避免意外行為並確保您的程式碼能如預期運作。
以上是為什麼 foreach 迴圈中的所有 lambda 表達式會捕捉相同的變數而不是它們各自的值?的詳細內容。更多資訊請關注PHP中文網其他相關文章!