我有以下C#程序:
class Program
{
static int[] foo = new int[1];
static void Main()
{
foo[0] = Get();
Console.WriteLine(foo[0]); //print "0"
}
static int Get()
{
foo = new int[1];
return 5;
}
}
从Get()
调用Main()
后,我希望foo
数组在第一个位置包含5
。但是,它是零。为什么会这样?
就像C#在调用foo
方法时捕获对Get()
的引用。
然后,此旧数组引用用于分配Get()
返回值(而不是在Get()
中实例化的新值)。
答案 0 :(得分:5)
语句中的操作数始终从左到右计算,即使运算符的优先规则决定事情如何处理会导致复杂的逻辑。这意味着foo[0]
目标实际上是在<{>}之前之前执行。你可以在IL中看到这个:
Get
请注意,.method private hidebysig static
void Main () cil managed
{
// Method begins at RVA 0x2050
// Code size 25 (0x19)
.maxstack 8
IL_0000: ldsfld int32[] Program::foo
IL_0005: ldc.i4.0
IL_0006: call int32 Program::Get()
IL_000b: stelem.i4
IL_000c: ldsfld int32[] Program::foo
IL_0011: ldc.i4.0
IL_0012: ldelem.i4
IL_0013: call void [mscorlib]System.Console::WriteLine(int32)
IL_0018: ret
} // end of method Program::Main
和ldsfld ... foo
(常数为零)在ldc.i4.0
之前执行,这些值留在堆栈上以供在稍后call ... Get()
。在更复杂的情况下(涉及括号等),看到编译器用于保留从左到右评估的技巧可能会非常有趣。像sharplab这样的工具可以帮助解决这个问题。 here's your example in sharplab
这是由于ECMA 334规范中的12.4运算符:
12.4.1一般
...
表达式中运算符的求值顺序取决于运算符的优先级和关联性(第12.4.2节)。
表达式中的操作数从左到右进行计算。 [例如:在F(i)+ G(i ++)* H(i)中,使用旧的i值调用方法F,然后使用旧值i调用方法G,并且最后,使用i的新值调用方法H.这与运算符优先级分开并且与运算符优先级无关。 结束示例]