@evildictaitor: Don't underestimate the C# compiler wrt the switch statement. A few examples:
Switches with a low number of cases can become if statements.
Switches with reasonably contigious ranges can be turned into the switch instruction. Holes can be patched with a jump target to the default case.
Switches with a non-zero case can get rebased using an add or sub instruction in order to use the switch instruction again.
Switches with sparse case values can be turned into nested/hierarchical switch instructions, using any of the above techniques applied.
Switches on string cases can become if statements, but if there are more than a treshold number (currently 6 IIRC), a Dictionary<string, int> is lazily constructed, using the int values for switch instruction use.
Just a little example:
static void Bar(int i, out int j)
j = 1;
j = 2;
j = 3;
j = 4;
j = 0;
Code above got compiled without /o+, so you'll see redundant nop instruction and excessive st/ld pairs in the IL. Using SOS, we dump the IL and the generated assembler:
Notice the sub instruction to rebase the switch statement and the insertion of case 18 equals default case by a jump to IL_0037 in the switch instruction's table. Case 25 which is further away from the switch range encountered here is handled by fall-through after the switch instruction and a jump to IL_0032. If you'd have a cluster of cases close to 25 again, you'd see another switch instruction being born.
I'll leave it as a good SOS exercise to the reader to perform a !U on the JITTED code address for different platform architectures and unravvle the assember generated...
0:004> !u 000007ff001501d0
Normal JIT generated code
Program.Bar(Int32, Int32 ByRef)
Begin 000007ff001501d0, size 62
Also don't forget order of cases in a switch statement doesn't matter, so the techniques described above can be applied even if cases don't appear in a sorted lexical order.