我有以下功能:
public string MyPhrase(int val)
{
if ((val %3 == 0) && (val % 6 == 0))
return "Fizz Bang";
if (val % 3 == 0)
return "Fizz";
if (val % 6 == 0)
return "Bang";
return "";
}
编译完并用IlDasm查看后,我得到了这个输出:
IL_0000: ldarg.1
IL_0001: ldc.i4.3
IL_0002: rem
IL_0003: brtrue.s IL_0010
IL_0005: ldarg.1
IL_0006: ldc.i4.6
IL_0007: rem
IL_0008: brtrue.s IL_0010
IL_000a: ldstr "Fizz Bang"
IL_000f: ret
IL_0010: ldarg.1
IL_0011: ldc.i4.3
IL_0012: rem
IL_0013: brtrue.s IL_001b
IL_0015: ldstr "Fizz"
IL_001a: ret
IL_001b: ldarg.1
IL_001c: ldc.i4.6
IL_001d: rem
IL_001e: brtrue.s IL_0026
IL_0020: ldstr "Bang"
IL_0025: ret
IL_0026: ldstr ""
IL_002b: ret
从this page看,如果属实,brtrue
会分支。让我感到困惑的是IL_0003
行。它说它应该分支到行IL_0010
如果它是真的。在我的C#代码中,我使用&&
来比较两个表达式,然后我的程序流应该跳转到下一个if
。它似乎倒退了。从发布的MSIL代码来看,它似乎检查我的操作是否为真,如果是,它跳转到我的下一个if
块,这在逻辑上是错误的。有人可以解释我错过的东西吗?
答案 0 :(得分:4)
这是短路评估的副作用用于&&运营商。如果左侧是假的,那么承诺不会评估运算符的右侧表达式。因此,如果REM操作码返回非零结果,则会看到它跳过val%6 == 0表达式并返回“Fizz Bang”语句。
答案 1 :(得分:1)
brtrue.s 如果堆栈上的值不为零,则将控制转移到给定目标。
因此,在您的IL代码IL_0003
中,转移控件为IL_0010
。表示(val%3 == 0)条件返回false。你在条件&&
的第一个条件if ((val %3 == 0) && (val % 6 == 0))
中使用(val % 3 == 0)
所以,在第一个条件IL_0005: ldarg.1
IL_0006: ldc.i4.6
IL_0007: rem
IL_0008: brtrue.s IL_0010
上返回false,第二个条件不会检查
IL_0010
并控制转移到IL_0010: ldarg.1
IL_0011: ldc.i4.3
IL_0012: rem
IL_0013: brtrue.s IL_001b
{{1}}
请查看此信息以获取更多信息:http://weblogs.asp.net/kennykerr/archive/2004/09/23/introduction-to-msil-part-6-common-language-constructs.aspx
答案 2 :(得分:0)
这个blok在开始比较之前加载来自Stack的参数数据
因此跳转到加载数据的这一部分是正常的
IL_0010: ldarg.1
IL_0011: ldc.i4.3
...