C# Closures, why is the loopvariable captured by reference?

前端 未结 6 1528
南笙
南笙 2020-11-30 11:39

In this example, I\'m attempting to pass by value, but the reference is passed instead.

for (int i = 0; i < 10; i++)
{
    Thread t = new Thread(() =>         


        
6条回答
  •  庸人自扰
    2020-11-30 12:17

    This is easier to understand if you look at what happens, in terms of scope:

    for (int i = 0; i < 10; i++)
    {
        Thread t = new Thread(() => new PhoneJobTest(i);    
        t.Start();
    }
    

    Basically translates to something very close to this:

    int i = 0;
    while (i < 10)
    {
        Thread t = new Thread(() => new PhoneJobTest(i);    
        t.Start();
        i++;
    }
    

    When you use a lambda expression, and it uses a variable declared outside of the lambda (in your case, i), the compiler creates something called a closure - a temporary class that "wraps" the i variable up and provides it to the delegate generated by the lambda.

    The closure is constructed at the same level as the variable (i), so in your case:

    int i = 0;
    ClosureClass = new ClosureClass(ref i); // Defined here! (of course, not called this)
    while (i < 10)
    {
        Thread t = new Thread(() => new PhoneJobTest(i);    
        t.Start();
        i++;
    }
    

    Because of this, each Thread gets the same closure defined.

    When you rework your loop to use a temporary, the closure is generated at that level instead:

    for (int i = 0; i < 10; i++)
    {
        int jobNum = i;
        ClosureClass = new ClosureClass(ref jobNum); // Defined here!
        Thread t = new Thread(() => new PhoneJobTest(jobNum);    
        t.Start();
    }
    

    Now, each Thread gets its own instance, and everything works properly.

提交回复
热议问题