2009-01-16 22 views
18

我一直在學習C#,並且我正試圖理解lambda。在下面的這個例子中,它打印出10次10​​次。如何告訴lambda函數在C#中捕獲副本而不是參考?

class Program 
{ 
    delegate void Action(); 
    static void Main(string[] args) 
    { 
     List<Action> actions = new List<Action>(); 

     for (int i = 0; i < 10; ++i) 
      actions.Add(()=>Console.WriteLine(i)); 

     foreach (Action a in actions) 
      a(); 
    } 
} 

顯然,拉姆達後面生成的類被存儲的引用或指針int i可變的,並且每當分配一個新的值,以相同的附圖的循環迭代。有沒有辦法迫使LAMDA搶副本,而不是一樣的C++ 0x語法

[&](){ ... } // Capture by reference 

[=](){ ... } // Capture copies 
+0

您可能想要閱讀[本文](http://csharpindepth.com/Articles/Chapter5/Closures.aspx),由我們自己的Jon Skeet編寫。 – 2009-01-16 20:12:41

+0

[C#捕獲變量循環]可能的重複(http://stackoverflow.com/questions/271440/c-sharp-captured-variable-in-loop) – nawfal 2013-11-02 06:35:41

回答

18

編譯器正在做的是將您的lambda和由lambda捕獲的任何變量拉入編譯器生成的嵌套類。

編譯後您的例子看起來很像這樣的:

class Program 
{ 
     delegate void Action(); 
     static void Main(string[] args) 
     { 
       List<Action> actions = new List<Action>(); 

       DisplayClass1 displayClass1 = new DisplayClass1(); 
       for (displayClass1.i = 0; displayClass1.i < 10; ++displayClass1.i) 
         actions.Add(new Action(displayClass1.Lambda)); 

       foreach (Action a in actions) 
         a(); 
     } 

     class DisplayClass1 
     { 
       int i; 
       void Lambda() 
       { 
         Console.WriteLine(i); 
       } 
     } 
} 

通過內部的for循環進行復印時,編譯器在每次迭代中生成新的對象,像這樣:

for (int i = 0; i < 10; ++i) 
{ 
    DisplayClass1 displayClass1 = new DisplayClass1(); 
    displayClass1.i = i; 
    actions.Add(new Action(displayClass1.Lambda)); 
} 
7

我已經能夠找到的唯一解決方案是使本地備份:

for (int i = 0; i < 10; ++i) 
{ 
    int copy = i; 
    actions.Add(() => Console.WriteLine(copy)); 
} 

但我無法理解爲什麼把一個副本的for循環中比具有拉姆達捕獲i任何不同。

9

唯一的解決辦法是在lambda內做一個本地拷貝和引用。在閉包中訪問時,C#(和VB.Net)中的所有變量都將具有引用語義與複製/值語義。無論哪種語言都無法改變這種行爲。

注意:它實際上並未作爲參考進行編譯。編譯器將變量提升爲閉包類,並在給定閉包類中將「i」的訪問重定向到字段「i」。儘管如此,將其視爲參考語義通常更容易。