В заключение, что запускает новый экземпляр захваченной переменной?

Я читаю книгу Джона СкитаC # в глубине.

На странице 156 у него есть пример, Листинг 5.13 «Захват нескольких экземпляров переменных с несколькими делегатами».

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]();

В объяснении после этого листинга он говорит, что «в каждом случае экземпляры делегата захватывали разные переменные».

Я понимаю это достаточно хорошо, потому что я понимаю, что каждый раз, когда вы закрываете переменную, компилятор генерирует IL, который инкапсулирует его в новый класс, созданный специально для того, чтобы эта переменная была захвачена (по сути, делая ее ссылочным типом, чтобы значение, на которое он ссылается) чтобы не быть уничтоженным с фреймом стека текущей выполняемой области видимости).

Но затем он говорит о том, что случилось бы, если бы мы захватилиindex непосредственно вместо созданияcounter переменная - «все делегаты имели бы одну и ту же переменную».

Это я не понимаю. неindex в том же объеме, что иcounter? Почему бы компилятору не создавать новый экземплярindex для каждого делегата?

Примечание: Я думаю, я понял это, когда набрал этот вопрос, но я оставлю этот вопрос здесь для потомков. Я думаю, что ответ таковindex на самом деле в другом объеме, какcounter, Индекс по существу объявляется "вне" цикла for ... это всегда одна и та же переменная.

Взгляните на IL, сгенерированный дляfor цикл, это доказывает, что переменные объявлены вне цикла (length а такжеi переменные были объявлены вfor объявление цикла).

.locals init (
    [0] int32 length,
    [1] int32 i,
    [2] bool CSЯ понимаю это достаточно хорошо, потому что я понимаю, что каждый раз, когда вы закрываете переменную, компилятор генерирует IL, который инкапсулирует его в новый класс, созданный специально для того, чтобы эта переменная была захвачена (по сути, делая ее ссылочным типом, чтобы значение, на которое он ссылается) чтобы не быть уничтоженным с фреймом стека текущей выполняемой области видимости).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

Одна вещь, я думаю, что книга могла бы быть лучше в этом вопросе, это действительно объяснить, что делает компилятор, потому что вся эта «магия» имеет смысл, если вы понимаете, что компилятор переносит закрытую переменную в новый класс.

Пожалуйста, исправьте любые заблуждения или недоразумения, которые у меня могут быть. Кроме того, не стесняйтесь уточнять и / или дополнять мое объяснение.

Ответы на вопрос(2)

Ваш ответ на вопрос