Lamda引起的一个问题

撰写于 2019-07-03 修改于 2019-07-03 分类 C# 标签 Lamda

##Lamda引起的一个问题

在C#中, Lamda表达式已经不是什么新鲜的东西,广泛使用在各个地方,跟 Action Delegate Func等这些委托没有本质却别,最近在使用是,遇到了常见的一种问题,在其他脚本Lua,Js中都能遇到,面试中也常遇到,就记录一下。先看一下代码演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static void Main(string[] args)
{
List<Action> callFunc = new List<Action>();
for (int i = 0; i < 5; i++)
{
callFunc.Add(() => Console.WriteLine("current index is:" + i));
}

foreach (var item in callFunc)
{
item();
}

Console.ReadKey();
}

以上代码输出是什么呢?预期的输出是:

current index is:0

current index is:1

current index is:2

current index is:3

current index is:4

但实际输出是:

current index is:5

current index is:5

current index is:5

current index is:5

current index is:5

为什么会这样呢?因为Lamda的一个特性:保存上下文。就是会保留当前执行环境内所有变量的值,在Lamda表达式中依旧可以访问这些变量。注意:是访问这些值,而不是复制这些值。当执行callFunc中的Lamda表达式时,会去寻找定义该表达式时上下文中存在的i,显然,i是在定义Lamda表达式的for循环中声明的,在内存中只有一份这样的数据,所有Lamda表达式访问的都是这同一个数据,当for循环执行完时:i=5,因此所有Lamda表达式中访问到的i值都是5。

那怎么才能得到预期结果呢?很显然,我们需要为每个Lamda创造一个单独的执行环境,也就是说,每个执行环境中的i都是一份拷贝,互不相干。因此需要包装一下Lamda表达式, 方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static void Main(string[] args)
{
List<Action> callFunc = new List<Action>();

Func<int, Action> generateCallFunc = (i) => { return () => Console.WriteLine("current index is:" + i); };

for (int i = 0; i < 5; i++)
{
callFunc.Add(generateCallFunc(i));
}

foreach (var item in callFunc)
{
item();
}

Console.ReadKey();
}

generateCallFunc就是一个函数,传入i,这样生成的每个Lamda就会有一个i的拷贝,每个Lamda在执行时,就会访问当前上下文中的i,这样就互不干涉,可以达到预期结果!

目录

Site by ZHJ using Hexo & Random

Hide