MethodBody.LocalVariables Count сбивает с толку

Для простого метода без локальных переменных, подобного следующему

public static int Test1(short i, long j)
{
    j = i + j;

    switch (j)
    {
    case 1:
        j = 2;
        break;
    default:
        j = 11;
        break;
    }

    return j;
}

Количество MethodInfo.GetMethodBody().LocalVariables.Count = 2 ПОЧЕМУ? Добавьте еще один оператор switch, и счет станет 3 ПОЧЕМУ?

public static int Test1(short i, long j)
{
    j = i + j;

    switch (j)
    {
    case 1:
        j = 2;
        break;
    default:
        j = 11;
        break;
    }

    switch (i)
    {
    case 1:
        j = 2;
        break;
    default:
        j = 11;
        break;
    }

    return j;
}

Локальные переменные не определены. Так почему 2 и 3. Кроме того, если другой оператор switch с j сохраняет счет на 2.

1 ответ

Думаю, следует ожидать, что компилятор C# генерирует локальные переменные, которых нет в исходном коде C#. Это потому, что стек IL не всегда является отличным местом для хранения некоторых временных значений, потому что вы можете получить доступ только к его вершине.

И это особенно относится к отладочным сборкам, потому что они оптимизированы для отладки, а не для производительности или использования памяти. Я понятия не имею, как эти местные жители помогают отладчику, или помогают ли они вообще, но я предполагаю, что они действительно имеют свою точку зрения.

В частности, ваш метод на самом деле не будет компилироваться, как указал jmh_gr, потому что вы не можете неявно приводить long в int, Если я изменю тип j в int, он создает такой код при использовании конфигурации отладки (декомпилируется с помощью Reflector, с отключенной оптимизацией):

public static int Test1(short i, int j)
{
    int CS$1$0000;
    int CS$4$0001;
    j = i + j;
    CS$4$0001 = j;
    if (CS$4$0001 != 1)
    {
        goto Label_0013;
    }
    j = 2;
    goto Label_0019;
Label_0013:
    j = 11;
Label_0019:
    CS$1$0000 = j;
Label_001D:
    return CS$1$0000;
}

Итак, вы видите, метод на самом деле имеет два локальных, и оба используются. При использовании конфигураций выпуска у сгенерированного IL есть только одна локальная переменная, и это выглядит так:

public static int Test1(short i, int j)
{
    int CS$0$0000;
    j = i + j;
    CS$0$0000 = j;
    if (CS$0$0000 != 1)
    {
        goto Label_0010;
    }
    j = 2;
    goto Label_0014;
Label_0010:
    j = 11;
Label_0014:
    return j;
}

Похоже, что местные не должны быть необходимыми, но, возможно, есть веская причина для этого. И, конечно, для производительности действительно важна компиляция JIT, а не код IL.

Другие вопросы по тегам