了解循环内变量的局部函数捕获
Understanding Local Function Capturing of Variables inside Loops
我注意到以下几点。此 C# 代码:
List<Action> methodList = new List<Action>();
for (int iFn = 0; iFn < 4; ++iFn)
{
void thisLocalFunction()
{
string output = iFn.ToString();
Console.WriteLine(output);
}
methodList.Add(thisLocalFunction);
}
for (int iFn = 0; iFn < methodList.Count; ++iFn)
{
methodList[iFn]();
}
生成 4、4、4、4。另一方面,此代码:
List<Action> methodList = new List<Action>();
for (int iFn = 0; iFn < 4; ++iFn)
{
string output = iFn.ToString();
void thisLocalFunction()
{
Console.WriteLine(output);
}
methodList.Add(thisLocalFunction);
}
for (int iFn = 0; iFn < methodList.Count; ++iFn)
{
methodList[iFn]();
}
生成 0、1、2、3。
我不确定我明白为什么。我读过一些关于“捕获”的内容,但我不确定这是否与这里发生的事情有关。谁能详细说明为什么这两种实现的行为不同?
您移动的关键代码位,string output = iFn.ToString();
将 iFn
变成字符串 当它是 运行
在第一个示例中,它是 运行 在循环完成后 (但“神奇地”,单个 iFn
仍然可用)。当然,循环结束后,iFn 为 4(因为循环就是这样停止的)。为什么循环结束后是运行?因为您“创建了一个方法并将其存储在一个变量中”,并且将 iFn
转换为字符串的调用是 在此方法中 。您没有 运行 您在循环中创建的方法,因此 iFn 在您在循环中时从未转换为字符串。变量中的方法之后只有 运行,并且因为该方法包含将 iFn
转换为字符串的代码,所以它以它具有的任何当前值访问 iFn
,这是 4.
在第二个例子中,iFn 被转换为一个字符串因为循环正在执行所以值是 0,然后是 1,然后是 2,然后是 3。这被存储在一个您在循环中创建的方法可以访问的新变量(称为 output
)(再次,假设该方法仍然可以访问 output
,即使它看起来超出范围,“通过magic"), 但 output
的值是在循环的每次传递中生成的,然后将其提供给方法。本质上,存储在列表中的 4 个方法变量中的每一个都可以访问一个名为 output
的 different 变量 - 第一个方法的输出值为 0 ,第二个方法的变量 output
的值为 1..
您可以设想,当您创建一个稍后将调用的方法时,该方法在创建时可以访问的“环境变量”与它打包在一起,因此它有自己的小环境在第一种情况下,4 种方法中的每一种都可以访问 iFn
现在的任何值,而在第二种情况下,它们可以访问 output
时的任何值在循环。仅仅因为变量在循环的每次传递中都被命名为相同并不意味着它重复使用相同的内存位置来保存数据
我注意到以下几点。此 C# 代码:
List<Action> methodList = new List<Action>();
for (int iFn = 0; iFn < 4; ++iFn)
{
void thisLocalFunction()
{
string output = iFn.ToString();
Console.WriteLine(output);
}
methodList.Add(thisLocalFunction);
}
for (int iFn = 0; iFn < methodList.Count; ++iFn)
{
methodList[iFn]();
}
生成 4、4、4、4。另一方面,此代码:
List<Action> methodList = new List<Action>();
for (int iFn = 0; iFn < 4; ++iFn)
{
string output = iFn.ToString();
void thisLocalFunction()
{
Console.WriteLine(output);
}
methodList.Add(thisLocalFunction);
}
for (int iFn = 0; iFn < methodList.Count; ++iFn)
{
methodList[iFn]();
}
生成 0、1、2、3。
我不确定我明白为什么。我读过一些关于“捕获”的内容,但我不确定这是否与这里发生的事情有关。谁能详细说明为什么这两种实现的行为不同?
您移动的关键代码位,string output = iFn.ToString();
将 iFn
变成字符串 当它是 运行
在第一个示例中,它是 运行 在循环完成后 (但“神奇地”,单个 iFn
仍然可用)。当然,循环结束后,iFn 为 4(因为循环就是这样停止的)。为什么循环结束后是运行?因为您“创建了一个方法并将其存储在一个变量中”,并且将 iFn
转换为字符串的调用是 在此方法中 。您没有 运行 您在循环中创建的方法,因此 iFn 在您在循环中时从未转换为字符串。变量中的方法之后只有 运行,并且因为该方法包含将 iFn
转换为字符串的代码,所以它以它具有的任何当前值访问 iFn
,这是 4.
在第二个例子中,iFn 被转换为一个字符串因为循环正在执行所以值是 0,然后是 1,然后是 2,然后是 3。这被存储在一个您在循环中创建的方法可以访问的新变量(称为 output
)(再次,假设该方法仍然可以访问 output
,即使它看起来超出范围,“通过magic"), 但 output
的值是在循环的每次传递中生成的,然后将其提供给方法。本质上,存储在列表中的 4 个方法变量中的每一个都可以访问一个名为 output
的 different 变量 - 第一个方法的输出值为 0 ,第二个方法的变量 output
的值为 1..
您可以设想,当您创建一个稍后将调用的方法时,该方法在创建时可以访问的“环境变量”与它打包在一起,因此它有自己的小环境在第一种情况下,4 种方法中的每一种都可以访问 iFn
现在的任何值,而在第二种情况下,它们可以访问 output
时的任何值在循环。仅仅因为变量在循环的每次传递中都被命名为相同并不意味着它重复使用相同的内存位置来保存数据