. net Выражение основного переключателя увеличивается с неправильным значением - PullRequest
0 голосов
/ 21 января 2020

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

Следующее Код демонстрирует это. InstructionPointer имел значение 2, opcode имел значение 6, что означает, что будет использоваться OpcodeJumpIfFalse. Функция Jif() вызывается просто отлично, и она возвращает значение, в моем случае она возвращала 0. Jif() также изменил значение InstructionPointer, изменив его значение на 9. InstructionPointer будет увеличено на 0 (возвращаемое значение Jif()), и я ожидаю, что его значение будет 9, но его значение go вернется к 2.

         InstructionPointer += opcode switch
         {
            OpcodeAdd => Add(),
            OpcodeMultiply => Mul(),
            OpcodeInput => Inp(),
            OpcodeOutput => Out(),
            OpcodeJumpIfTrue => Jit(),
            OpcodeJumpIfFalse => Jif(),
            OpcodeLessThan => Let(),
            OpcodeEquals => Equ(),
            OpcodeAdjustRelativeBase => Arb(),
            OpcodeHalt => End(),
            _ => throw new ArgumentOutOfRangeException()
         };

Минимальный пример, показывающий то же поведение:

         int j = 2;
         int i = 1;

         int A()
         {
            j = 10;
            return 0;
         }

         j += i switch
         {
            1 => A(),
            _ => throw new Exception()
         };

         Console.WriteLine(j);

Я заметил, что Resharper (в минимальном примере) говорит мне, что назначение не используется в A().

Мой вопрос: почему это происходит? Значение j "захвачено" перед переключателем? Это ожидаемое поведение?

На данный момент я изменил свой код, чтобы использовать временную переменную, которая решает проблему, но я все еще хотел бы знать, что здесь происходит.

1 Ответ

6 голосов
/ 21 января 2020

Помните, что составной оператор присваивания a += b совпадает с a = a + b (за исключением того, что a вычисляется только один раз), см. §7.17.2 спецификации c.

Вот немного более простой пример, который не использует переключатель и имеет тот же эффект:

int j = 2;

int A()
{
    j = 10;
    return 0;
}

j = j + A();

Если вы не хотите думать с точки зрения локальных функций, вы также можете написать это как класс:

class C
{
    private int j;

    public void Test()
    {
        j = 2;
        j = j + A();
    }

    private int A()
    {
        j = 10;
        return 0;
    }
}

Компилятор будет:

  1. Начать с оценки j + A():
    1. Pu sh текущего значения j, которое 2, в стек
    2. Вызов A(), который устанавливает j в 10 и возвращает 0
    3. Pu sh возвращаемое значение A(), которое равно 0, в стек
    4. Добавьте вместе два значения в стеке: 2 + 0
  2. Присвойте это значение j

Если вы напишите присвоение наоборот, как j = A() + j, то его окончательное значение будет 10. Если вы выполните ту же последовательность шагов, что и выше, вы поймете, почему.


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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...