1. 程式人生 > >Lambda表示式和For迴圈使用需要注意的一個地方

Lambda表示式和For迴圈使用需要注意的一個地方

一個需要注意的地方
看下面的程式碼:


using System;
using System.Collections.Generic;
using System.Linq;

namespace MyCsStudy
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Func<int>> list = new List<Func<int>>();

            for (int i = 0; i < 3; i++)
            {
                list.Add(() => i); //list依次新增0,1,2??
            }
            foreach (var item in list)
            {
                Console.WriteLine(item()); //輸出的竟然是?
            }
        }
    }
}

執行試一下看看結果,如果你以前碰到過js裡的閉包問題,相信你不會大驚小怪(而且可能已經知道了問題的原因),但是,如果你從來沒有碰到過這種情況,是不是令你大吃一驚?!輸出的竟然不是0,1,2,而是三個3,oh,my god。緊接著,立刻,你會大膽想到這裡的list在Add方法執行的地方Add進去的是一個引用型別(這裡是lambda表示式()=>i),它們執行的結果共同指向值為3的同一個引用地址!
沒錯,我們詳細分析一下:
1、我們首先定義一個list,其儲存格式為func<int>,即返回int型的代理;然後,用for迴圈將i封裝進lambda表示式,並加入到該list中,最後,用foreach迴圈輸出結果。
2、因為lambda表示式實質就是個委託,也就指向一個匿名函式,所以,在foreach輸出的時候,使用item()來呼叫它,讓它所指向的函式執行。
至於第2步中item()執行的結果為什麼都是3,原因是這樣的:
(1)在for迴圈中,只能有一個 i 變數。即在第一次迴圈時,i 的地址就分配好了(注意了,這裡i的地址第一次分配後是不變的),不會因為迴圈次數的多少而發生任何改變,其改變的只能是裡面裝載的值。
(2)lambda表示式在構造時, 傳進去的是變數的地址,而不是具體值。只有當真正執行這個lambda表示式時,才會去確定它的值。這就是為什麼上面的例子中,其結果均為3(for迴圈在最後,當i=2時,i又加了1)。

那麼如何解決這個問題?解決方案很簡單:

1是在for迴圈中,定義一臨時變數tmpNum,儲存i的值即可。因為編譯器會對該臨時變數重新分配記憶體,這樣,每次迴圈,都重新分配新的記憶體,就不會有這個問題了。

2用foreach也是同樣的道理