IL if-comparison相反

时间:2015-03-10 12:57:15

标签: c# il intermediate-language

我只是想进入IL,因为我正在使用代码注入。我需要分析代码并涵盖各种情况。

遗憾的是,如果最后一条指令在if子句中,那么最后注入一个方法调用并不起作用,因为那时调用包含了该调用。

现在我一直在分析if-code被翻译成IL并且我对如何完成这一点感到有些困惑。显然编译器会反转if。这是出于性能原因吗?如果是这样,这会改善表现吗?

亲眼看看:

        string test;
        Random rnd = new Random();
        bool b = rnd.Next(0, 10) == 3;
        if (b)
        {
            // TRUE
            test = "True branch";
            // END TRUE
        }
        else
        {
            // FALSE
            test = "False branch";
            //END FALSE
        }

这是输出:

    IL_0000: nop
    IL_0001: newobj instance void [mscorlib]System.Random::.ctor()
    IL_0006: stloc.1
    IL_0007: ldloc.1
    IL_0008: ldc.i4.0
    IL_0009: ldc.i4.s 10
    IL_000b: callvirt instance int32 [mscorlib]System.Random::Next(int32,  int32)
    IL_0010: ldc.i4.3
    IL_0011: ceq
    IL_0013: stloc.2
    IL_0014: ldloc.2
    IL_0015: ldc.i4.0
    IL_0016: ceq
    IL_0018: stloc.3
    IL_0019: ldloc.3
    IL_001a: brtrue.s IL_0026

    IL_001c: nop
    IL_001d: ldstr "True branch"
    IL_0022: stloc.0
    IL_0023: nop
    IL_0024: br.s IL_002e

    IL_0026: nop
    IL_0027: ldstr "False branch"
    IL_002c: stloc.0
    IL_002d: nop

    IL_002e: ret

正如您所看到的,在将Random结果与const 3进行比较之后,它再次与0进行比较,从而反转结果,相当于if (false)

这是什么原因?由于您需要额外的说明,它的性能是否较差?这总是发生吗?

2 个答案:

答案 0 :(得分:1)

您正在查看调试版本。将其更改为发布版本,并使用brfalse.s

IL_0000: newobj instance void [mscorlib]System.Random::.ctor()
IL_0005: stloc.1
IL_0006: ldloc.1
IL_0007: ldc.i4.0
IL_0008: ldc.i4.s 10
IL_000a: callvirt instance int32 [mscorlib]System.Random::Next(int32, int32)
IL_000f: ldc.i4.3
IL_0010: ceq
IL_0012: stloc.2
IL_0013: ldloc.2
IL_0014: brfalse.s IL_001e

IL_0016: ldstr "True branch"
IL_001b: stloc.0
IL_001c: br.s IL_0024

IL_001e: ldstr "False branch"
IL_0023: stloc.0

我添加了Console.WriteLine,否则删除了test变量。

IL_0024: ldloc.0
IL_0025: call void [mscorlib]System.Console::WriteLine(string)
IL_002a: ret

所以Debug和release之间的区别是:

// Debug
IL_0015: ldc.i4.0
IL_0016: ceq
IL_0018: stloc.3
IL_0019: ldloc.3
IL_001a: brtrue.s IL_0026

VS

// Release
IL_0014: brfalse.s IL_001e

所以有四个关于Debug版本的附加指令,以及一个" inverse"如果

首先我要说C#编译器试图保持代码的顺序与它的编写顺序相同。首先," true"分支,那么"假"分支。

好的......我做了一个假设......

让我们说问题是在调试模式下......在调试模式下,代码必须是详细的...非常详细。所以

if (b)

被翻译为

if (b == true)

遗憾的是true是"什么 - 但是0",所以写起来更容易

if (!(b == false))

因为false是" 0"。但这是在调试模式下编写的:-)只有调试模式使用临时变量

as

// bool temp = b == false;
IL_0015: ldc.i4.0
IL_0016: ceq
IL_0018: stloc.3
IL_0019: ldloc.3

// if (temp) // go to else branch
IL_001a: brtrue.s IL_0026

答案 1 :(得分:0)

编译器没有反转任何东西。请注意,if语句的两个分支在IL中的顺序与源代码中的顺序相同。你可以从两个字符串的顺序看到。

当布尔值为false时,brtrue的使用只是分支的自然IL。测试真值的布尔值意味着将它与0进行比较。值0为假,其他任何东西都被认为是真的。

因此,编译器发出IL以与0进行比较。如果该比较为真,即如果布尔值的序数值为0,则布尔值为false。因此,如果等于零,则这是你所拥有的,如果布尔值为假,则表示分支。这是ceq对0,然后是brtrue

那就是说,值得指出的是,编译调试性能不是问题。编译器希望编写代码以使调试器可以检查变量。如果您对性能感兴趣,则必须从发布版本中查看IL。当你这样做时,你会看到完全不同的代码。