IL 如果比较颠倒

本文关键字:比较 如果 IL | 更新日期: 2024-11-08 06:49:23

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

可悲的是,如果最后一条指令在 if 子句中,则在末尾注入方法调用是行不通的,因为该调用当时被偏执所包含。

现在我一直在分析代码是否被翻译成 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

如您所见,在随机结果与常量 3 进行比较后,它会再次与 0 进行比较,从而反转等效于 if (false) 的结果。

这是什么原因?由于您需要其他说明,它的性能会降低吗?这种情况总是发生吗?

IL 如果比较颠倒

您正在查看调试版本。将其更改为发布版本,并使用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
IL_0015: ldc.i4.0
IL_0016: ceq
IL_0018: stloc.3
IL_0019: ldloc.3
IL_001a: brtrue.s IL_0026

// Release
IL_0014: brfalse.s IL_001e

因此,调试版本的四个附加指令,以及一个"反向"if。

首先,我要说的是,C# 编译器尝试将代码保持与编写顺序相同的顺序。所以首先是"真"分支,然后是"假"分支。

还行。。。我正在做一个假设...

假设问题在调试模式下...在调试模式下,代码必须非常详细...很啰嗦。所以

if (b)

被翻译成

if (b == true)

可悲的是,true是"除 0 之外的任何内容",因此编写起来更容易

if (!(b == false))

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

// 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

编译器没有反转任何内容。请注意,if 语句的两个分支在 IL 中的显示顺序与它们在源代码中的显示顺序相同。你可以从两个字符串的顺序中看到这一点。

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

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

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