简体   繁体   English

C#Closure没有按预期工作

[英]C# Closure is not working as expected

I can't understand quite clearly the difference between two blocks of code. 我无法理解两块代码之间的区别。 Consider there is a program 考虑有一个程序

    class Program
{
    static void Main(string[] args)
    {

        List<Number> numbers = new List<Number>
                                   {
                                       new Number(1),
                                       new Number(2),
                                       new Number(3)
                                   };


        List<Action> actions = new List<Action>();
        foreach (Number numb in numbers)
        {
            actions.Add(() => WriteNumber(numb));
        }

        Number number = null;
        IEnumerator<Number> enumerator = numbers.GetEnumerator();
        while (enumerator.MoveNext())
        {
            number = enumerator.Current;
            actions.Add(() => WriteNumber(number));
        }

        foreach (Action action in actions)
        {
            action();
        }

        Console.ReadKey();


    }

    public static void WriteNumber(Number num)
    {
        Console.WriteLine(num.Value);
    }

    public class Number
    {
        public int Value;

        public Number(int i)
        {
            this.Value = i;
        }

    }

}

The output is 输出是

1
2
3
3
3
3    

These two blocks of code should work identically. 这两个代码块应该完全相同。 But you can see that the closure is not working for the first loop. 但是你可以看到闭包不适用于第一个循环。 What am i missing? 我错过了什么?

Thanks in advance. 提前致谢。

You declare the number variable outside of your while loop. 您在while循环之外声明了number变量。 For each number you store the reference of it in your number variable - every time overwriting the last value. 对于每个数字,您将其引用存储在number变量中 - 每次覆盖最后一个值。

You should just move the declaration inside the while-loop, so you have a new variable for each of your numbers. 您应该只在while循环中移动声明,因此每个数字都有一个新变量。

    IEnumerator<Number> enumerator = numbers.GetEnumerator();
    while (enumerator.MoveNext())
    {
        Number number = enumerator.Current;
        actions.Add(() => WriteNumber(number));
    }

These two blocks of code should work identically. 这两个代码块应该完全相同。

No they shouldn't - at least in C# 5. In C# 3 and 4 they would , in fact. 不,他们不应该 - 至少在C#5中。事实上,他们在C#3和4中。

But in the foreach loop, in C# 5, you have one variable per iteration of the loop. 但是在foreach循环中,在C#5中,每次迭代循环都有一个变量。 Your lambda expression captures that variable. 您的lambda表达式捕获该变量。 Subsequent iterations of the loop create different variables which don't affect the previously-captured variable. 循环的后续迭代创建不同的变量,这些变量不会影响先前捕获的变量。

In the while loop, you have one variable which all the iterations capture. while循环中,您有一个变量可以捕获所有迭代。 Changes to that variable will be seen in all of the delegates that captured it. 对于该变量的更改将在捕获它的所有代理中看到。 You can see this by adding this line after your while loop: 你可以通过在while循环后添加这一行来看到这一点:

number = new Number(999);

Then your output would be 然后你的输出将是

1
2 
3
999
999
999

Now in C# 3 and 4, the foreach specification was basically broken by design - it would capture a single variable across all iterations. 现在在C#3和4中, foreach规范基本上被设计破坏了 - 它将在所有迭代中捕获单个变量。 This was then fixed in C# 5 to use a separate variable per iteration, which is basically what you always want with that sort of code. 然后在C#5中对此进行了修复,以便每次迭代使用一个单独的变量,这基本上就是您总是想要的那种代码。

In your loop: 在你的循环中:

    Number number = null;
    IEnumerator<Number> enumerator = numbers.GetEnumerator();
    while (enumerator.MoveNext())
    {
        number = enumerator.Current;
        actions.Add(() => WriteNumber(number));
    }

number is declared outside of the loop scope. number在循环范围之外声明。 So when it gets set to the next current iterator, all your action refernces to number also get updated to the latest. 因此,当它设置为下一个当前迭代器时,所有操作引用的数字也会更新到最新。 So when you run each action, they will all use the last number. 因此,当您运行每个操作时,它们都将使用最后一个数字。

Thanks for all your answers. 谢谢你的所有答案。 But I think I was misunderstood. 但我觉得我被误解了。 I WANT the closue to work. 我想要关闭工作。 That's why i set the loop variable out of scope. 这就是我将循环变量设置在范围之外的原因。 The question is: Why does not it work in the first case? 问题是:为什么它不适用于第一种情况? I forgot to mention that I use C# 3.5 (not C# 5.0). 我忘了提到我使用的是C#3.5(不是C#5.0)。 So the soop variable should be defined out of scope and two code blocks shoul work identically. 因此,应该在范围之外定义soop变量,并且两个代码块应该相同地工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM