View C # (1) Through IL -- switch statement (I)

Source: Internet
Author: User
View C # (1) Through IL)
Switch Statement (I)

Address: http://www.cnblogs.com/AndersLiu/archive/2008/11/03/csharp-via-il-switch-1.html

Original: Anders Liu

Abstract: The switch statement is a common jump statement in C #. Different codes can be executed based on different values of a parameter. This article introduces the IL code generated by the compiler when different types of parameters are passed into the switch statement. This section describes how to use the integer and enumeration types in the switch statement.

A switch statement is a common jump statement in C #. You can execute different codes based on different values of a parameter. The switch statement can have multiple branches. That is to say, you can jump to N code segments to run according to N values of the parameter. This is different from the if statement. A separate if statement has only two branches (because the if statement parameter can have only true or false values), unless nested if statements are used.

The parameters that the switch statement can accept are limited. In short, they can only be integers, enumerations, or strings. This article describes the switch statements of integer, enumeration, and string types.

Switch command

Before getting started, let's briefly introduce the switch commands in the IL assembly language. The switch command (note that it is separated from the switch statement in C #) is a multi-branch command in IL. Its basic form is as follows:

Switch (Label_1, Label_2, Label_3 ...)

Where switch is the keyword IL, Label_1 ~ Label_N is a series of labels (the same as the labels used in the goto statement), which indicate the position in the code. The operating principle of this command is that an unsigned integer value pops up from the top of the computing stack. If the value is 0, it is redirected to the position specified by Label_1 for execution. If it is 1, to Labe_2; to 2, to Label_3; and so on.

If the value popped up at the top of the stack is not in the range of the label list (0 ~ N-1), ignore the switch command, jump to a command after the switch command to start execution. Therefore, for the switch command, its "default clause" is at the beginning.

In addition, the label position referenced by Label_x can be located only in the current method body and does not have to be behind the switch command.

Okay. We will see the switch command instance later.

Switch Statement of Integer type

Code 1-use the switch statement of an integer type parameter. The value is continuous.

<Br/> static void TestSwitchInt (int n) <br/>{< br/> switch (n) <br/>{< br/> case 1: Console. writeLine ("One"); break; <br/> case 2: Console. writeLine ("Two"); break; <br/> case 3: Console. writeLine ("Three"); break; <br/>}< br/>

The switch statement in code 1 accepts the parameter n of the int type, and we observe that the values in each case clause are continuous. Write this code in a complete program and compile it. Then use ildasm to open the generated assembly. The corresponding IL code is shown in Code 2.

Code 2-The IL code generated by code 1

<Br/>. method private hidebysig static void TestSwitchInt (int32 n) di-managed <br/>{< br/> // Code size 56 (0x38) <br/>. maxstack 2 <br/>. locals init (int32 V_0) <br/> IL_0000: ldarg.0 <br/> IL_0001: stloc.0 <br/> IL_0002: ldloc.0 <br/> IL_0003: ldc. i4.1 <br/> IL_0004: sub <br/> IL_0005: switch (<br/> IL_0017, <br/> IL_0022, <br/> IL_002d) <br/> IL_0016: ret <br/> IL_0017: ldstr "One" <br/> IL_001c: call void [mscorlib] System. console: WriteLine (string) <br/> IL_0021: ret <br/> IL_0022: ldstr "Two" <br/> IL_0027: call void [mscorlib] System. console: WriteLine (string) <br/> IL_002c: ret <br/> IL_002d: ldstr "Three" <br/> IL_0032: call void [mscorlib] System. console: WriteLine (string) <br/> IL_0037: ret <br/>}// end of method Program: TestSwitchInt <br/>

We can see that the first two lines of code, IL_0000 and IL_0001, store the parameter n in a local variable, and then lines IL_0002 to IL_0004 subtract the value of this variable by 1, and leave the result at the top of the computing stack. Aha, the value of the parameter is less than 1. Will the situations to be judged become 0, 1, and 2? Yes. In the next switch command, three addresses IL_0017, IL_0022, and IL_002d are provided for these three values. The code at these three addresses is the code to be executed when the values are 1, 2, and 3.

The above is the case where the value is continuous. What if the values in each case clause are not consecutive? Let's take a look at the following C # code:

Code 3-switch statement Using Integer Parameters with Discontinuous values

<Br/> static void TestSwitchInt2 (int n) <br/> {<br/> switch (n) <br/>{< br/> case 1: Console. writeLine ("1"); break; <br/> case 3: Console. writeLine ("3"); break; <br/> case 5: Console. writeLine ("5"); break; <br/>}< br/>

In code 3 compiled programs, the IL code generated by the compiler is as follows:

Code 4-The IL code generated by code 3

<Br/>. method private hidebysig static void TestSwitchInt2 (int32 n) di-managed <br/>{< br/> // Code size 64 (0x40) <br/>. maxstack 2 <br/>. locals init (int32 V_0) <br/> IL_0000: ldarg.0 <br/> IL_0001: stloc.0 <br/> IL_0002: ldloc.0 <br/> IL_0003: ldc. i4.1 <br/> IL_0004: sub <br/> IL_0005: switch (<br/> IL_001f, // 0 <br/> IL_003f, // 1 <br/> IL_002a, // 2 <br/> IL_003f, // 3 <br/> IL_0035) // 4 <br/> IL_001e: ret <br/> IL_001f: ldstr "1" <br/> IL_0024: call void [mscorlib] System. console: WriteLine (string) <br/> IL_0029: ret <br/> IL_002a: ldstr "3" <br/> IL_002f: call void [mscorlib] System. console: WriteLine (string) <br/> IL_0034: ret <br/> IL_0035: ldstr "5" <br/> IL_003a: call void [mscorlib] System. console: WriteLine (string) <br/> IL_003f: ret <br/>}// end of method Program: TestSwitchInt2 <br/>

Seeing code 4, the first thought is that the number of Jump addresses in the switch command does not match the value in the switch statement in the C # program. However, after careful observation, we can find that the switch command provides different jump addresses for the values 0, 2, and 4 (case 1, 3, and 5 in the switch statement. The same address IL_003f is provided for values 1 and 3 (not in the switch statement). Check whether the address is ret.

That is to say, when the values are not continuous, the compiler will automatically fill the "gap" in the switch instruction with the address of the "default clause ". Of course, because code 4 is too simple, the "gap value" directly jumps to the end of the method.

What if the value is more discontinuous? In this case, there will be a large number of "gap values" in the switch command ". You must know that the switch command and the subsequent jump address list are both part of the command. Increasing the gap value will inevitably lead to an increase in the Assembly volume. You don't have to worry. The compiler is very clever. Please refer to the following code:

Code 5-use the switch statement of an integer parameter. The value is not consecutive.

<Br/> static void TestSwitchInt3 (int n) <br/> {<br/> switch (n) <br/>{< br/> case 10: Console. writeLine ("10"); break; <br/> case 30: Console. writeLine ("30"); break; <br/> case 50: Console. writeLine ("50"); break; <br/>}< br/>

In code 5, the values given in each case clause of the switch statement differ by 20, which means that if the "gap value" described above is used, the switch command contains up to 41 jump addresses, but only 3 are valid. But modern compilers obviously won't make such low-level errors. The IL generated by the compiler for Code 5 is as follows:

Code 6-Code 5: The IL code generated

<Br/>. method private hidebysig static void TestSwitchInt3 (int32 n) di-managed <br/>{< br/> // Code size 51 (0x33) <br/>. maxstack 2 <br/>. locals init (int32 V_0) <br/> IL_0000: ldarg.0 <br/> IL_0001: stloc.0 <br/> IL_0002: ldloc.0 <br/> IL_0003: ldc. i4.s 10 <br/> IL_0005: beq. s IL_0012 <br/> IL_0007: ldloc.0 <br/> IL_0008: ldc. i4.s 30 <br/> IL_000a: beq. s IL_001d <br/> IL_000c: ldloc.0 <br/> IL_000d: ldc. i4.s 50 <br/> IL_000f: beq. s IL_0028 <br/> IL_0011: ret <br/> IL_0012: ldstr "10" <br/> IL_0017: call void [mscorlib] System. console: WriteLine (string) <br/> IL_001c: ret <br/> IL_001d: ldstr "30" <br/> IL_0022: call void [mscorlib] System. console: WriteLine (string) <br/> IL_0027: ret <br/> IL_0028: ldstr "50" <br/> IL_002d: call void [mscorlib] System. console: WriteLine (string) <br/> IL_0032: ret <br/>}// end of method Program: TestSwitchInt3 <br/>

From Code 6, we can find that the switch command is missing and the beq. s command is issued in Xi'an at IL_0005, IL_000a, and IL_000f respectively. This command is a brief form of beq command. When the difference between the jump position and the current position is within the range of an sbyte type, the compiler automatically selects the short form to narrow the size of the instruction set. Beq commands are used to retrieve two values from the computing stack for comparison. If the two values are equal, they are redirected to the target location (with the parameter specified after the beq command) for execution, otherwise, the execution starts from the next command of the beq command.

It can be seen that when the value of the switch statement is not continuous, the compiler will discard the switch command and use a series of conditional jumps for implementation. This is somewhat similar to the if-else if-...-else statement.

Use the switch statement of Enumeration type

Enumeration in. NET is a special value type. It must use an integer type as its underlying type (underlying type ). Therefore, during the operation, enumeration is treated as an integer. The switch command automatically converts the enumeration value at the top of the stack to an unsigned integer and then judges the result.

Therefore, the use of enumeration in the switch statement is not much different from the use of the integer type. See the following code:

Code 7-use the enumeration type in the switch statement

<Br/> static void TestSwitchEnum (Num n) <br/> {<br/> switch (n) <br/>{< br/> case Num. one: Console. writeLine ("1"); break; <br/> case Num. two: Console. writeLine ("2"); break; <br/> case Num. three: Console. writeLine ("3"); break; <br/>}< br/>

The Num type is an enumeration, which is defined as public enum {One, Two, Three}

The following is the IL code generated by the compiler for code 7:

Code 8-code 7: The IL code generated

<Br/>. method private hidebysig static void TestSwitchEnum (valuetype andsliu. CSharpViaIL. switch. num n) Pencil managed <br/>{< br/> // Code size 54 (0x36) <br/>. maxstack 1 <br/>. locals init (valuetype andsliu. CSharpViaIL. switch. num V_0) <br/> IL_0000: ldarg.0 <br/> IL_0001: stloc.0 <br/> IL_0002: ldloc.0 <br/> IL_0003: switch (<br/> IL_0015, <br/> IL_0020, <br/> IL_002b) <br/> IL_0014: ret <br/> IL_0015: ldstr "1" <br/> IL_001a: call void [mscorlib] System. console: WriteLine (string) <br/> IL_001f: ret <br/> IL_0020: ldstr "2" <br/> IL_0025: call void [mscorlib] System. console: WriteLine (string) <br/> IL_002a: ret <br/> IL_002b: ldstr "3" <br/> IL_0030: call void [mscorlib] System. console: WriteLine (string) <br/> IL_0035: ret <br/>}// end of method Program: TestSwitchEnum <br/>

Code 8 is essentially different from Code 2. This is because the enumerated values are treated as integers. In addition, if the enumerated values are not consecutive, the generated code is similar to code 4 and 6.

Summary

This article describes how the compiler translates switch statements of the integer type. If you care about the minimal improvement in efficiency, remember:

  • Use Continuous values in the switch whenever possible;
  • If the values are not continuous, use the case clause as few as possible and put the case with a high frequency before it (because the switch statement is similar to the if-else Statement ).

Returned Directory: view C # Through IL #

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.