0

我注意到以下内容。这个 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。

我不确定我明白为什么。我读过一些关于“捕获”的文章,但我不确定这是否与这里发生的事情有关。有人可以告诉我为什么这两种实现的行为不同吗?

4

1 回答 1

1

您移动的关键代码在运行时string output = iFn.ToString();变成iFn了一个字符串

在第一个示例中,它在循环完成后运行(但“魔术”iFn仍然可用)。当然,循环结束后,iFn 为 4(因为这就是循环停止的方式)。为什么它在循环完成后运行?因为您“创建了一个方法并将其存储在一个变量中”,并且iFn转换为字符串的调用在此方法中。您没有在循环中运行您创建的方法,因此 iFn 在循环中从未变成字符串。变量中的方法仅在之后运行,并且由于该方法包含iFn转换为字符串的代码,因此它可以访问iFn它具有的任何当前值,即 4。

在第二个示例中,当循环执行时, iFn 被转换为字符串,因此值是 0,然后是 1,然后是 2,然后是 3。这存储在一个新变量(称为output)中,您在循环中创建的方法具有访问(再次,假设该方法仍然可以访问output,即使它看起来超出范围,“通过魔术”),但是在output循环的每次传递中都会生成 的值,然后将其提供给该方法。本质上,存储在列表中的 4 个方法作为变量中的每一个都可以访问一个名为的不同变量output——第一个方法的输出值为 0,调用的第二个方法的变量output值为 1..

您可以设想,当您创建一个稍后将调用的方法时,该方法在创建时可以访问的“环境变量”与它一起打包,以便它有自己的小环境可以执行。在第一种情况,4 个方法中的每一个都可以访问iFn它现在的任何值,而在第二种情况下,它们可以访问output它在循环中时的任何值。仅仅因为变量在循环的每次传递中都被命名为相同并不意味着它正在重用相同的内存位置来保存数据

于 2020-07-26T17:36:13.720 回答