sub*_*ary 0 c# lambda closures local-functions
我在玩我的 intcode 计算机实现(从代码 2019 的出现开始),发现当我实现开关(选择要执行的操作)时,它采用了错误的值。
下面的代码演示了这一点。
InstructionPointer有一个值2,opcode有值6意味着OpcodeJumpIfFalse将被使用。函数Jif()被调用得很好,它返回一个值,在我的例子中它返回0. Jif()还修改了 的值InstructionPointer,将其值更改为9。该InstructionPointer会的被增加0(返回值Jif()),我会想到它的价值是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()
};
Run Code Online (Sandbox Code Playgroud)
显示相同行为的最小示例:
int j = 2;
int i = 1;
int A()
{
j = 10;
return 0;
}
j += i switch
{
1 => A(),
_ => throw new Exception()
};
Console.WriteLine(j);
Run Code Online (Sandbox Code Playgroud)
我确实注意到 Resharper(在最小的例子中)告诉我分配在A().
我的问题是,为什么会发生这种情况?是j在切换前“捕获”的值吗?这是预期的行为吗?
现在,我已将代码更改为使用临时变量,这解决了问题,但我仍然想知道这里发生了什么。
请记住,复合赋值运算符a += b与a = a + b(除了a仅评估一次)相同,请参阅规范的第 7.17.2 节。
这是一个稍微简单的示例,它不使用开关,但具有相同的效果:
int j = 2;
int A()
{
j = 10;
return 0;
}
j = j + A();
Run Code Online (Sandbox Code Playgroud)
如果你不想考虑局部函数,你也可以把它写成一个类:
class C
{
private int j;
public void Test()
{
j = 2;
j = j + A();
}
private int A()
{
j = 10;
return 0;
}
}
Run Code Online (Sandbox Code Playgroud)
编译器将:
j + A():
j,即2,压入堆栈A(),它设置j为10并返回0A(),即0,压入堆栈2 + 0j如果你反过来写赋值,作为j = A() + j,那么它的最终值是10。如果您遵循与上述相同的步骤顺序,您就会明白原因。
这种通用方法——将变量更改为表达式的副作用,同时也会更改该变量——是一个坏主意。它会导致非常难以理解的代码。
| 归档时间: |
|
| 查看次数: |
68 次 |
| 最近记录: |