为了账号安全,请及时绑定邮箱和手机立即绑定

C#中循环中的捕获变量

C#中循环中的捕获变量

蝴蝶不菲 2019-05-24 14:47:55
C#中循环中的捕获变量我遇到了一个关于C#的有趣问题。我有如下代码。List<Func<int>> actions = new List<Func<int>>();int variable = 0;while (variable < 5){     actions.Add(() => variable * 2);     ++ variable;}foreach (var act in actions){     Console.WriteLine(act.Invoke());}我希望它输出0,2,4,6,8。但是,它实际输出5个10。似乎是由于所有操作都涉及一个捕获的变量。结果,当它们被调用时,它们都具有相同的输出。有没有办法解决这个限制,让每个动作实例都有自己的捕获变量?
查看完整描述

4 回答

?
呼啦一阵风

TA贡献1802条经验 获得超6个赞

是 - 在循环中获取变量的副本:

while (variable < 5){
    int copy = variable;
    actions.Add(() => copy * 2);
    ++ variable;}

您可以将其视为C#编译器每次命中变量声明时都创建一个“新”局部变量。实际上它会创建适当的新闭包对象,如果你在多个范围内引用变量,它会变得复杂(在实现方面),但是它可以工作:)

请注意,此问题更常见的是使用forforeach

for (int i=0; i < 10; i++) // Just one variable
foreach (string x in foo) // And again, despite how it reads out loud

有关详细信息,请参阅C#3.0规范的第7.14.4.2节,关于闭包的文章也有更多示例。


查看完整回答
反对 回复 2019-05-24
?
慕婉清6462132

TA贡献1804条经验 获得超2个赞

在幕后,编译器生成一个表示方法调用闭包的类。它为循环的每次迭代使用闭包类的单个实例。代码看起来像这样,这使得更容易看到错误发生的原因:

void Main(){
    List<Func<int>> actions = new List<Func<int>>();

    int variable = 0;

    var closure = new CompilerGeneratedClosure();

    Func<int> anonymousMethodAction = null;

    while (closure.variable < 5)
    {
        if(anonymousMethodAction == null)
            anonymousMethodAction = new Func<int>(closure.YourAnonymousMethod);

        //we're re-adding the same function 
        actions.Add(anonymousMethodAction);

        ++closure.variable;
    }

    foreach (var act in actions)
    {
        Console.WriteLine(act.Invoke());
    }}class CompilerGeneratedClosure{
    public int variable;

    public int YourAnonymousMethod()
    {
        return this.variable * 2;
    }}

这实际上不是您的示例中的已编译代码,但我已经检查了自己的代码,这看起来非常类似于编译器实际生成的内容。


查看完整回答
反对 回复 2019-05-24
  • 4 回答
  • 0 关注
  • 653 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信