IL开关指令

IL switch instruction

我正在研究从此 C# 代码生成的 IL 代码 (RELEASE):

int a = Convert.ToInt32(Console.ReadLine());
        
switch(a)
{
    case 1:  Console.WriteLine(); break;
    case 2:  Console.WriteLine(); break;
    case 3:  Console.WriteLine(); break;
    case 15: Console.WriteLine(); break;
    default: Console.WriteLine(); break;
}

IL:

//Why not "switch(IL_0026, IL_002c, IL_0032, IL_0038)"?
IL_000e: switch (IL_0026, IL_002c, IL_0032)

IL_001f: ldloc.0
IL_0020: ldc.i4.s 15
IL_0022: beq.s IL_0038

IL_0024: br.s IL_003e

IL_0026: call void [System.Console]System.Console::WriteLine()
IL_002b: ret

IL_002c: call void [System.Console]System.Console::WriteLine()
IL_0031: ret

IL_0032: call void [System.Console]System.Console::WriteLine()
IL_0037: ret

IL_0038: call void [System.Console]System.Console::WriteLine()
IL_003d: ret

IL_003e: call void [System.Console]System.Console::WriteLine()
IL_0043: ret

为什么 IL 中的“开关”使用序列 (1,2,3...n+1) 并且不添加其他值(例如:对于 15 生成的 3 条指令(001f、0020、0022))

//Why not "switch(IL_0026, IL_002c, IL_0032, IL_0038)"?

因为如果a的值是4,而不是15,就会跳转到第4个标签。switch中的跳转目标对应连续的值 的测试值。

为了使用 switch 并且没有 15 的额外特殊情况,它需要是:

switch(IL_0026, IL_002c, IL_0032, IL_003e, IL_003e, IL_003e, IL_003e, IL_003e, IL_003e, IL_003e, IL_003e, IL_003e, IL_003e, IL_003e, IL_0038)

(希望我没记错)

这将是一个更大的跳跃 table。


我总是建议您查看 Reflection.Emit 的文档以了解您正在查看的说明,例如OpCodes.Switch,以确保您了解指令的作用。

The switch instruction pops a value off the stack and compares it, as an unsigned integer, to N. If value is less than N, execution is transferred to the target indexed by value, where targets are numbered from 0 (for example, a value of 0 takes the first target, a value of 1 takes the second target, and so on).