传递给委托时,整数作为引用类型处理

Rik*_*erg 3 c# delegates

本周我参加了荷兰的TechDays 2013,我得到了一个有趣的测验问题.问题是:以下程序的输出是什么.这是代码的样子.

class Program
{
    delegate void Writer();

    static void Main(string[] args)
    {
        var writers = new List<Writer>();
        for (int i = 0; i < 10; i++)
        {
            writers.Add(delegate { Console.WriteLine(i); });
        }

        foreach (Writer writer in writers)
        {
            writer();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

显然,我给出的答案是错误的.我认为,因为int是一个值类型,传递的实际值Console.WriteLine()被复制,所以输出将是0 ... 9.但是i在这种情况下作为参考类型处理.正确答案是它会显示十次10.有人可以解释为什么以及如何解释?

Ser*_*rvy 6

我认为,因为int是一个值类型,所以传递给Console.WriteLine()的实际值会被复制

这是完全正确的. 当您调用WriteLine该值时将被复制.

所以,你WriteLine什么时候打电话?它不在for循环中.你不是在那个时候写任何东西,你只是在创建一个代表.

foreach在调用委托时,直到循环时,才会将变量中的值i复制到堆栈以进行调用WriteLine.

那么,循环i期间的价值是foreach什么?对于循环的每次迭代,它是10 foreach.

所以现在你要问," 好吧,i在这期间有什么foreach loop, isn't it out of scope.嗯,不,它不是.这证明是一个"闭包".当一个匿名方法引用变量的范围需要持续的变量时,匿名方法,可以是任何时间段.如果没有什么特别的事情可以完成,那么变量将是随机垃圾,包含任何碰巧被困在内存中的位置.C#主动确保情况不会发生.

那它是做什么的?它创建了一个闭包类; 它是一个包含许多字段的类,代表所有被关闭的字段.换句话说,代码将被重构为如下所示:

public class ClosureClass
{
    public int i;

    public void DoStuff()
    {
        Console.WriteLine(i);
    }
}

class Program
{
    delegate void Writer();

    static void Main(string[] args)
    {
        var writers = new List<Writer>();
        ClosureClass closure = new ClosureClass();
        for (closure.i = 0; closure.i < 10; closure.i++)
        {
            writers.Add(closure.DoStuff);
        }

        foreach (Writer writer in writers)
        {
            writer();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们都有一个匿名方法的名称(所有匿名方法都由编译器给出一个名称),我们可以确保变量将存在,只要引用匿名函数的委托存在.

看看这个重构器,我希望很清楚为什么结果会10被打印10次.