为什么每个循环输出 10?int不是值类型吗?

Dou*_*ell 3 c# value-type reference-type

我浏览了一些 C# 示例并发现了以下内容:

using System;
using System.Collections.Generic;

namespace ConsoleApp1
{
    class Program
    {
        delegate void Printer();

        static void Main()
        {
            List<Printer> printers = new List<Printer>();

            for (int i = 0; i < 10; i++)
            {
                printers.Add(delegate { var d = i; Console.WriteLine(d); });
            }

            foreach (var printer in printers)
            {
                printer();
            }
            Console.ReadLine();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我期望它0通过输出9,因为 anint是一种值类型,d应该设置为i当时的任何值。

然而,这输出了10十倍。

为什么是这样?int 不是一个引用而是在委托内部吗?

注意:我并不是想在这里解决问题,只是以可重复应用的方式了解它是如何工作的。

编辑:我的困惑的例子

int i = 9;
int d = 8;
d = i;
i++;
Console.WriteLine(d);
Run Code Online (Sandbox Code Playgroud)

这表明它i是作为值传递的,而不是引用。我预计封口内也会有同样的情况,但感到惊讶。


感谢您的评论,我现在了解更多了,委托中的代码直到之后才执行,并且它使用i编译器生成的泛型类中存在的代码?

在 javascript 中,同样类型的代码输出 1-9,这正是我在 C# 中所期望的。https://jsfiddle.net/L21xLaq0/2/。

Evk*_*Evk 5

您可能有兴趣了解编译器实际上如何重写这段代码,因为它有助于理解发生了什么。如果您编译然后在禁用“花式视图”的某些反编译器(如 dotPeek)中查看 dll,您将看到以下内容(某些名称已更改,因为它们不可读):

class Program {
    delegate void Printer();

    private static void Main() {
        List<Program.Printer> printerList = new List<Program.Printer>();
        // closure object which holds captured variables
        Program.DisplayClass10 cDisplayClass10 = new Program.DisplayClass10();
        int i;
        // loop assigns field of closure object
        for (cDisplayClass10.i = 0; cDisplayClass10.i < 10; cDisplayClass10.i = i + 1) {
            // your delegate is method of closure object
            printerList.Add(new Program.Printer(cDisplayClass10.CrypticFunctionName));
            i = cDisplayClass10.i;
        }
        // here, cDisplayClass10.i is 10
        foreach (Program.Printer printer in printerList)
            printer();
        Console.ReadLine();
    }

    // class for closure object
    [CompilerGenerated]
    private sealed class DisplayClass10 {
        public int i;

        internal void CrypticFunctionName() {
            Console.WriteLine(this.i);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)