.net 核心开关表达式以错误的值递增

.net core switch expression incrementing with wrong value

我在玩弄我的 intcode 计算机实现(从代码 2019 的出现开始),发现当我实现开关(select 要执行哪个操作)时它取了错误的值。

以下代码演示了这一点。 InstructionPointer 的值为 2opcode 的值为 6,这意味着将使用 OpcodeJumpIfFalse。函数 Jif() 被调用得很好,它 return 是一个值,在我的例子中它是 returning 0Jif() 还修改了 InstructionPointer 的值,将其值更改为 9InstructionPointer 将增加 0Jif() 的 return 值),我希望它的值是 9,但它的值会返回成为 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 "captured" 的值是在switch之前吗?这是预期的行为吗?

目前,我已经更改我的代码以使用临时变量,这解决了问题,但我仍然想知道这里发生了什么。

请记住,复合赋值运算符 a += ba = a + b 相同(除了 a 仅计算一次),请参阅规范的 §7.17.2。

这里有一个稍微简单的例子,它不使用开关,但效果相同:

int j = 2;

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

j = j + A();

如果你不想从局部函数的角度考虑,你也可以把它写成 class:

class C
{
    private int j;

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

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

编译器将:

  1. 首先评估 j + A()
    1. j 的当前值 2 压入堆栈
    2. 调用 A(),将 j 设置为 10 和 returns 0
    3. A() 的 return 值压入堆栈
    4. ,即 0
    5. 将堆栈中的两个值加在一起:2 + 0
  2. 将此值分配给 j

如果你反过来写赋值,如j = A() + j,那么它的最终值为10。如果按照与上述相同的步骤顺序进行操作,您就会明白原因了。


这种通用方法——将变量更改为同时更改该变量的表达式的副作用——是个坏主意。它导致非常难以理解的代码。