1. 程式人生 > >Lambda表達式和For循環使用需要註意的一個地方

Lambda表達式和For循環使用需要註意的一個地方

get targe space rgs namespace 道理 分享圖片 添加 有一個

一個需要註意的地方
看下面的代碼:

技術分享圖片
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也是同樣的道理

Lambda表達式和For循環使用需要註意的一個地方