最后,是什么触发捕获变量的新实例? [英] In closure, what triggers a new instance of the captured variable?

查看:66
本文介绍了最后,是什么触发捕获变量的新实例?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读Jon Skeet的深度的C#

I'm reading Jon Skeet's C# in Depth.

在第156页上,他有一个示例,清单5.13使用多个委托捕获多个变量实例化。

On page 156 he has an example, Listing 5.13 "Capturing multiple variable instantiations with multiple delegates".

List<ThreadStart> list = new List<ThreadStart>();

for(int index=0; index < 5; index++;)
{
    int counter = index*10;
    list.Add(delegate
          {
              Console.WriteLine(counter);
              counter++;
          }
        );
}

foreach(ThreadStart t in list)
{
    t();
}

list[0]();
list[0]();
list[0]();

list[1]();

在此列表后的解释中,他说:每个委托实例都捕获了一个不同的变量这种情况。

In the explanation after this listing, he says "each of the delegate instances has captured a different variable in this case."

我很了解这一点,因为我知道每次关闭变量时,编译器都会生成IL,并将其封装在专门允许的新类中要捕获的变量(基本上使其成为引用类型,以便所引用的值不会被当前执行范围的堆栈框架破坏)。

I understand this well enough because I understand that each time you close over a variable the compiler generates IL that encapsulates it in a new class made specifically to allow that variable to be captured (essentially making it a reference type so that the value it is referring to doesn't get destroyed with the stack frame of the currently executing scope).

但是随后他谈到了如果我们直接捕获 index 而不是创建 counter 变量会发生什么情况-全部代表将共享相同的变量。

But then he talks about what would have happened had we captured index directly instead of creating the counter variable - "all the delegates would have shared the same variable".

我不明白。 索引计数器不在同一个范围内吗?为什么编译器还不为每个委托创建一个 index 的新实例?

This I don't understand. Isn't index in the same scope as counter? Why would the compiler not also create a new instance of index for each delegate?

注意::我认为我在输入此问题时就已经知道了,但是我将在此保留该问题,以供后代参考。我认为答案是 index 实际上与 counter 的范围不同。索引本质上被声明为for循环的外部……每次都是相同的变量。

Note: I think I figured it out as I typed this question, but I will leave the question up here for posterity. I think the answer is that index is actually in a different scope as counter. Index is essentially declared "outside" the for loop...it is the same variable every time.

看看为 for 循环,它证明变量是在循环外部声明的( length i 是在 for 循环声明中声明的变量)。

Taking a look at the IL generated for a for loop, it proves the variables are declared outside the loop (length and i were variables declared in the for loop declaration).

.locals init (
    [0] int32 length,
    [1] int32 i,
    [2] bool CS$4$0000
)

IL_0000: nop
IL_0001: ldc.i4.s 10
IL_0003: stloc.0
IL_0004: ldc.i4.0
IL_0005: stloc.1
IL_0006: br.s IL_001b
// loop start (head: IL_001b)
    IL_0008: nop
    IL_0009: ldloca.s i
    IL_000b: call instance string [mscorlib]System.Int32::ToString()
    IL_0010: call void [mscorlib]System.Console::WriteLine(string)
    IL_0015: nop
    IL_0016: nop
    IL_0017: ldloc.1
    IL_0018: ldc.i4.1
    IL_0019: add
    IL_001a: stloc.1

    IL_001b: ldloc.1
    IL_001c: ldloc.0
    IL_001d: clt
    IL_001f: stloc.2
    IL_0020: ldloc.2
    IL_0021: brtrue.s IL_0008
// end loop






一件事我认为这本书可能在该主题上做得更好,实际上是在解释编译器在做什么,因为如果您理解编译器将close变量包装在新类中,那么所有这些魔术都是有道理的。


One thing I think the book might have done better regarding this subject is really explain what the compiler is doing, because all this "magic" makes sense if you understand that the compiler is wrapping the closed over variable in a new class.

请更正我可能存在的任何误解或误解。另外,请随时详细说明和/或增加我的解释。

Please correct any misconceptions or misunderstandings I might have. Also, feel free to elaborate on and/or add to my explanation.

推荐答案

听起来您已经制定了答案-您每次循环都不会获得新的 index 实例。如果您考虑允许在循环内修改 index 值的方式-例如。如果您要跳过项目,在某些情况下或将其设置为零,则可以将其递增-应该清楚的是,您只有一个 index ,而不是每次迭代都使用新的。

It sounds like you've worked out the answer - you don't get a new index instance each time round the loop. If you consider the ways you're allowed to modify the index value inside the loop - eg. you can increment it if you want to skip items, set it back to zero in some cases, or anything else you like - it should be clear that you've only got one instance of index, not a new one for every iteration.

另一方面,会创建一个新的计数器在每次迭代中-如果您在该循环的底部对其进行了更改,则它不会影响下一次迭代使用的 counter 变量。

On the other hand, a new counter is created on every iteration - if you made a change to it at the bottom of that loop, it would have no effect on the counter variable that the next iteration uses.

foreach 循环用于重用其循环变量,与 for 循环,这是人们的常见陷阱-参见 C#是否有理由在foreach中重用变量?

foreach loops used to reuse their loop variable, in the same way as for loops, and this was a common gotcha for people - see Is there a reason for C#'s reuse of the variable in a foreach?

Eric Lippert 解释他们已经更改了C#5中的 foreach 每次都获取一个新变量,并且他们将 for 保留为原样

Eric Lippert explains that they've changed foreach in C# 5 to get a new variable every time, and also that they're leaving for as-is.

这篇关于最后,是什么触发捕获变量的新实例?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆