c# - .net 核心开关表达式以错误的值递增
问题描述
我正在玩我的 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()
};
显示相同行为的最小示例:
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
是切换前“捕获”的值吗?这是预期的行为吗?
现在,我已将代码更改为使用临时变量,这解决了问题,但我仍然想知道这里发生了什么。
解决方案
请记住,复合赋值运算符a += b
与a = a + b
(除了a
只计算一次)相同,请参阅规范的第 7.17.2 节。
这是一个稍微简单的示例,它不使用开关,并且具有相同的效果:
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;
}
}
编译器将:
- 从评估开始
j + A()
:- 将 的当前值
j
, 即2
, 压入堆栈 - Call
A()
,设置并j
返回10
0
- 将 的返回值压
A()
入0
堆栈 - 将堆栈上的两个值相加:
2 + 0
- 将 的当前值
- 将此值分配给
j
如果你把赋值写成相反的方式,如j = A() + j
,那么它的最终值为10
。如果您按照与上述相同的步骤顺序进行操作,您就会明白原因。
这种通用方法——将变量更改为表达式的副作用也会更改该变量——是一个坏主意。它导致非常难以理解的代码。
推荐阅读
- sparql - 在 Wikidata API 中过滤搜索
- python - Python+Django:我在结帐时看不到我渲染的数据,但我可以在其他页面中看到它
- python - moviepy:从视频中提取音频并在另一个视频中使用它
- react-native - 不考虑父组件宽度的基于百分比的宽度(React Native)
- java - 赎金笔记-Leetcode
- swift - 在 Swift 中检查互联网访问(不仅仅是网络连接)
- python - 尝试为 pandas .to_sql 动态构建 dtype 输入字典
- c# - 客户端程序无法连接到托管在 0.0.0.0 上的服务器
- kotlin - 错误:值……。org.json.JSONArray 类型的不能转换为 JSONObject
- google-apps-script - Google Sheet Script:我可以通过脚本访问已经发生的单元格编辑历史吗