The switch Statement

   


The switch Statement

The switch statement enables a program to select from multiple actions, based on the value of a controlling expression also called a switch expression. The logic implemented by the switch statement is, therefore, somewhat similar to that of a multibranch if-else statement. To demonstrate the similarities and differences between a switch statement and a multibranch if-else statement, let's have a look at an example.

The program of Listing 8.10 implements the logic behind a "talking" dishwasher by using a multibranch if-else statement. By choosing one of the numbers 1, 2, or 3, the user can select one of the three programs featured by the dishwasher 1: Economy, 2: Intensive, 3: Universal. Every time a choice has been made, the dishwasher responds with a suitable comment.

Listing 8.10 DishwasherMultibranch.cs
01: using System; 02: 03: class DishwasherMultibranch 04: { 05:     public static void Main() 06:     { 07:         int programSelection; 08: 09:         Console.WriteLine("Please select a dishwashing program: "); 10:         Console.WriteLine("1: Economy   2: Intensive    3: Universal"); 11:         programSelection = Convert.ToInt32(Console.ReadLine()); 12: 13:         if(programSelection == 1) 14:         { 15:             Console.WriteLine("You have selected the Economy program"); 16:             Console.WriteLine("Nice environmentally friendly choice"); 17:         } 18:         else if (programSelection == 2) 19:         { 20:             Console.WriteLine("You have selected the Intensive program"); 21:             Console.WriteLine("Good for week old unwashed dishes"); 22:         } 23:         else if (programSelection == 3) 24:         { 25:             Console.WriteLine("You have selected the Universal program"); 26:             Console.WriteLine("OK! I'm ready for anything. Just try me out."); 27:         } 28:         else 29:             Console.WriteLine("Invalid selection. You must choose a number " + 30:                 "between 1 and 3"); 31:     } 32: } 

Sample output 1:

 Please select a dishwashing program: 2<enter> You have selected the Intensive program Good for week old unwashed dishes 

Sample output 2:

 Please select a dishwashing program: 9<enter> Invalid selection. You must choose a number between 1 and 3. 

I assume you are already familiar with the syntax and semantics of the multibranch if-else statement, so I will only highlight aspects relevant to the switch statement discussion.

Notice that each of the Boolean expressions in lines 13, 18, and 23 of the multibranch if-else statement

 programSelection == 1 programSelection == 2 programSelection == 3 

can be written in the following general form

 <Controlling expression> == <constant expression> 

In this context, it is important to observe that

  • The controlling expression remains the same; it's always represented by the int variable programSelection.

  • The constant expression is, as the name implies, always a constant. In this case, it is represented by the constants 1, 2, and 3, respectively.

  • The only comparison operator used is the equals (==) operator.

Thus, the program compares (for equality) the value of a single expression (in this case, programSelection) with several constant expressions (here 1, 2, and 3) to decide which statements to execute. The switch statement is tailor-made and confined to this type of scenario.

The switch statement condenses the type of multibranch if-else statements shown in Listing 8.10 through a somewhat rigid structure specialized for only this type of logic. Listing 8.11, along with its accompanying analysis, explains how this is done by implementing exactly the same functionality as the program in Listing 8.10, but this time with a switch statement.

Listing 8.11 DishwasherSwitch.cs
01: using System; 02: 03: class Dishwasher 04: { 05:     public static void Main() 06:     { 07:         int programSelection; 08: 09:         Console.WriteLine("Please select a dishwashing program: "); 10:         Console.WriteLine("1: Economy   2: Intensive    3: Universal"); 11:         programSelection = Convert.ToInt32(Console.ReadLine()); 12: 13:         switch(programSelection) 14:         { 15:             case 1: 16:                 Console.WriteLine("You have selected the Economy program"); 17:                 Console.WriteLine("Nice environmentally friendly choice"); 18:                 break; 19:             case 2: 20:                 Console.WriteLine("You have selected the Intensive program"); 21:                 Console.WriteLine("Good for week old unwashed dishes"); 22:                 break; 23:             case 3: 24:                 Console.WriteLine("You have selected the Universal program"); 25:                 Console.WriteLine("OK! I'm ready for anything. Just try me out."); 26:                 break; 27:             default: 28:                 Console.WriteLine("Invalid selection. You must choose a number " + 29:                     "between 1 and 3"); 30:                 break; 31:         } 32:     } 33: } 

The output is the same as the output of Listing 8.10.

The switch statement is located between line 13 containing the required switch keyword and line 31 with the ending brace matching that of line 14. Notice how programSelection only appears in one position next to the switch keyword (line 13), instead of in every single control expression as in Listing 8.10. The three constants, 1, 2, and 3, are now positioned after the required keyword case in lines 15, 19, and 23 and before a colon. There is no need to write the equality operator anywhere; it is implicitly understood that if programSelection is equal to 1, lines 16 18 will be executed. If programSelection is equal to 2, lines 20 22 are executed, and so on.

Braces to indicate blocks of statements, as seen with the multibranch if-else statements, are redundant. All statements between case and break are executed as if they resided inside a block.

Each of the lines 15, 19, 23, and 27 only act as line labels, not as delineators between choices. Thus, execution does not automatically end at the next case or default section. For example, execution must be stopped by a break statement, as in this example. Consequently, the break statements in lines 18, 22, 26, and 30 ends the switch statement and transfers the control to the next line after the switch statement (in this case, line 32).

The default case of the multibranch if-else statements specified by lines 28 30 of Listing 8.10 is in the switch statement implemented by the optional default section, as shown in lines 27 30.

Note

graphics/common.gif

If a switch statement does not contain the optional default section and none of the constant values are matching that of the control expression, the flow of control will continue from the next line after the closing brace of the switch statement.


Note

graphics/common.gif

Not only is the switch statement often clearer and easier to write than a multibranch if-else statement, the switch statement is also more efficient in most cases and, thus, creates faster running code.


The general syntax for the switch statement is displayed in Syntax Box 8.10.

Syntax Box 8.10 The switch Statement

 switch (<Switch_expression>) {      case <Constant_expression> :             [<Statement>;              <Statement>;                                         graphics/ccc.gif     ...               <Statement>;               <break_statement>; | <goto_statement>;]       case <Constant_expression> :             [<Statement>;              <Statement>;                       ...               <Statement>;               <break_statement>; | <goto_statement>;]      <Any number of case fragments can be included>       [default:              [ <Statement>;                <Statement>;                                       graphics/ccc.gif     ...                <Statement>;                <break_statement>; | <goto_statement>; ] ] }    <Zero or one default section may be included> 

Notes:

  • The term <Switch expression> used next to the switch keyword is the official term for what has been termed a control expression.

  • case and default sections are both referred to as switch sections.

  • The <Constant_expression> following the case keyword in a case section is referred to as a case value or a case label. Each case value must be unique.

  • The <brake_statement> and <goto_statement> constitute the most common way to terminate a switch section.

Tip

graphics/bulb.gif

Position the default section last.

Even though the case sections and the default section can appear in any sequence, it is considered a better style to position the default section at the end of the switch statement.


The notion of the flow of control moving from one switch section through to the next switch section is called fall through. The most common way to prevent fall through is by applying the break and goto statements.

Apart from the syntax just described, there are several rules you must follow when constructing switch statements. Let's have a look at each of them and their consequences.

switch Statement Rule 1

A switch section can consist of zero or more statements. Only switch sections containing zero statements are allowed to let the flow of control continue (fall through) to the next switch section.

By inserting case sections with zero statements one after the other, it is possible to let multiple constant values cause the same statements to be executed. This is illustrated in the code snippet in Listing 8.12. If letter of line 1 is equal to either 'a' or 'A', the statements in lines 5 and 6 will be executed. Similarly, if letter is equal to 'b', 'B', 'c', 'C', lines 11 and 12 are executed.

Listing 8.12 Several Letters Causing the Same Statement to Be Executed Incomplete Code
01: switch(letter) 02: { 03:     case 'a': 04:     case 'A': 05:         Console.WriteLine("The letter is either a or A"); 06:         break; 07:     case 'b': 08:     case 'B': 09:     case 'c': 10:     case 'C': 11:         Console.WriteLine("The letter is either b, B, c or C"); 12:         break; 13:     default: 14:         Console.WriteLine("Invalid input"); 15          break; 16: } 

Note that this is only an extract from a complete program and does not compile on its own.

Only the case sections commencing with case 'A': and case 'C': (lines 4 and 10) contain any statements, the rest in lines 3, 7, 8, and 9 have zero statements. In the latter cases, the flow of control is said to fall through down to the next case section. Consequently, if letter holds the character 'b', the flow of control falls through all the way down to case 'C':, executing the statements of this section.

switch Statement Rule 2

If there are one or more statements in a case section, the flow of control must not be able to reach the end of this section; in other words, a fall through is not permitted.

Typically, this is prevented through the use of a break or goto statement, but other constructs, such as the return statement, associated with the termination of methods, can also be used.

Listing 8.13 provides an example of an invalid switch section. The case 'a': section beginning in line 3 triggers a compiler error by allowing the flow of control to fall through and run all the way down to line 6.

Listing 8.13 Invalid Code Fall Through Not Permitted
... 01: switch(letter) 02: { 03:     case 'a': 04:         Console.WriteLine("You typed the letter: a"); 05:         Console.WriteLine("a is for apple"); 06:     case 'b': 07:         Console.WriteLine("You typed the letter: b"); 08:         Console.WriteLine("b is for boat"); 09:         break;  

Note

graphics/common.gif

The reason for disallowing fall through in switch sections with one or more statements originates from the bad experiences of programmers working with the switch statements of C++ and Java. Both these languages contain very similar switch statements to that of C#, but with one notable difference they both allow fall through, disregarding the number of statements involved. By mistakenly omitting a break statement, one or more switch sections might then be executed unknowingly and incorrectly. In other words, it is very easy to introduce bugs into the code when fall through is permitted. As a result, the designers of C# decided to eliminate this source of bugs in C#.


Why does C++ and Java allow fall through if it is a source of bugs? Sometimes it can be useful to let the value of a switch expression execute not only its own associated switch section but also another or several other switch sections. By intentionally omitting the break statement in C++ and Java, the programmer can implement this logic. But if C# does not allow fall through, how can similar logic be implemented with C#? This is where the goto statement comes into the picture.

Recall the goto statement and its ability to jump to a label statement positioned somewhere in the source code. C# allows the default keyword and the case keyword, along with its associated constant expression, to act as label statements for the goto statement. Apart from simulating a fall through, it is possible to redirect the flow from inside any switch section to the beginning of any switch section with the goto statement, irrespective of the sequence in which the switch statements appear in the source code.

Listing 8.14 illustrates this feature by calculating the manufacturing cost of the three different ice creams manufactured by Chilling Sensations Ltd. Bare Minimum, Standard, and Mumbo Jumble. Bare Minimum is just a very basic ice cream. Standard has exactly the same ingredients as Bare Minimum plus a few extra goodies. Mumbo Jumble is a deluxe ice cream identical to Standard but with quite a few additional scrumptious and decadent ingredients added. The manufacturing costs are specified as follows:

  • Bare Minimum The cost of manufacturing Bare Minimum.

  • Standard The cost of Bare Minimum plus the cost of the additional ingredients of Standard.

  • Mumbo Jumble The cost of Standard plus the cost of the additional ingredients of Mumbo Jumble.

Listing 8.14 prints out the calculated manufacturing costs for the ice cream specified by the user, as shown in the sample output that follows the listing.

Listing 8.14 IceCreamManufacturer.cs
01: using System; 02: 03: class IceCreamManufacturer 04: { 05:     public static void Main() 06:     { 07:         const decimal BareMinimumCost = 1.2m; 08:         const decimal StandardCost = 1.5m; 09:         const decimal MumboCost = 2.1m; 10:         int iceCreamType; 11:         decimal totalCost; 12: 13:         Console.WriteLine("Please enter ice cream type:\n" + 14:             "1: Mumbo Jumble  2: Standard  3: Bare Minimum"); 15:         iceCreamType = Convert.ToInt32(Console.ReadLine()); 16:         totalCost = 0; 17: 18:         switch(iceCreamType) 19:         { 20:             case 1: 21:                 totalCost = totalCost + MumboCost; 22:                 goto case 2; 23:             case 2: 24:                 totalCost = totalCost + StandardCost; 25:                 goto case 3; 26:             case 3: 27:                 totalCost = totalCost + BareMinimumCost; 28:                 Console.WriteLine("Total manufacturing cost: {0:C} ", totalCost); 29:                 break; 30:             default: 31:                 Console.WriteLine("Invalid selection"); 32:                 break; 33:         } 34:     } 35: } Please enter ice cream type: 1: Mumbo Jumble  2: Standard  3: Bare Minimum 1<enter> Total manufacturing cost: $4.80 

The goto case statement of line 22 lets the execution continue at case 2: in line 23, imitating a fall through. The same happens in line 25. So when the user, for example, requests to have the costs calculated for the Mumbo Jumble by entering 1, execution inside the switch statement commences at line 20 followed by all statements between line 20 and line 29, allowing the program to perform what amounts to the following calculation totalCost = MumboCost + StandardCost + BareMinimumCost, which is the cost of a Mumbo Jumble. Similarly, if 2 is entered to calculate the cost of Standard, the equivalent of the calculation totalCost = BareMinimumCost + StandardCost is performed.

Note

graphics/common.gif

Because a switch statement is a type of statement, it can be inserted at the position where a statement is expected in a switch section, resulting in nested switch statements. Even when constructing nested switch statements, it is only possible to let goto-case statements refer to case sections contained within the same switch statement as the goto-case statement. However, you can utilize the standard goto label statement to jump to an outer switch statement.

In general, though, stay away from the goto label statement and avoid nested switch statements with more than a couple of levels.


Listing 8.14 illustrated the use of goto-case statements. If you want to jump to a default section, simply exchange the case and constant expression with the keyword default, as shown in Syntax Box 8.11.

Syntax Box 8.11 The goto-Case-Statement and goto-Default-Statement

 goto_case_statement::=          goto case <Constant_expression>; goto_default_statement::=          goto default; 

switch Statement Rule 3

The governing type of the switch statement is determined by the switch expression. If the switch expression is of type byte, sbyte, short, ushort, int, uint, long, ulong, char, string, or enum, that is the governing type. If this is not the case, there must exist an implicit conversion path specified by the programmer to one of the types byte, sbyte, short, ushort, int, uint, long, ulong, char, string. (To see how user-defined implicit conversion paths are specified, see Chapter 14, "Class Anatomy Part III, Writing Intuitive Code.")

Thus, the floating point and decimal types are not permitted as switch expression types. If you need to implement the functionality of a switch statement but are faced with one of those types, you must resort to the multibranch if-else statement.

The enum type is, with its associated finite set of related constants, a very suitable type for a switch expression. Listing 8.15 determines the number of days in a particular month of a particular year by utilizing a switch expression of type enum. As the sample output illustrates, the user can type in the relevant year and the name of the month to receive the correct number of days.

Listing 8.15 DaysInMonth.cs
01: using System; 02: 03: class DaysInMonth 04: { 05:     enum Month {January, February, March, April, May, June, 06:         July, August, September, October, November, December} 07: 08:     public static void Main() 09:     { 10:         Month currentMonth; 11:         int year; 12:         int dayCount = 0; 13: 14:         Console.Write("Enter year: "); 15:         year = Convert.ToInt32(Console.ReadLine()); 16:         Console.Write("Enter month: "); 17:          //Convert user input to enum type Month 18:         currentMonth = (Month)Enum.Parse(typeof(Month), Console.ReadLine(), true); 19:         switch (currentMonth) 20:         { 21:             case Month.January: 22:             case Month.March: 23:             case Month.May: 24:             case Month.July: 25:             case Month.August: 26:             case Month.October: 27:             case Month.December: 28:                 dayCount = 31; 29:                 break; 30:             case Month.April: 31:             case Month.June: 32:             case Month.September: 33:             case Month.November: 34:                 dayCount = 30; 35:                 break; 36:             case Month.February: 37:                  //Determine if year is a leap year 38:                 if (((year % 4 == 0) && !(year % 100 == 0)) || (year % 400 == 0)) 39:                     dayCount = 29; 40:                 else 41:                     dayCount = 28; 42:                 break;  43:             default: 44:                 Console.WriteLine("Invalid month"); 45:                 break; 46:         } 47:         Console.WriteLine("Number of days in {0}  of year {1} : {2} ", 48:             currentMonth, year, dayCount); 49:     } 50: } Enter year: 2000<enter> Enter month: November<enter> Number of days in November of year 2001: 30 

Lines 5 and 6 declare the enum Month to contain our familiar months of the year as its members.

Line 18 is only explained very briefly. It utilizes the static method of the System.Enum class called Parse() to convert the string input from the user (via Console.ReadLine()) into one of the valid values of type Month. true specified at the end of the line simply indicates that uppercase and lowercase characters are treated as identical characters. In other words, if the user inputs January or january, this string value is converted to Month.January and inserted into the variable currentMonth. All months containing 31 days are lined up in lines 21 27 letting the execution fall through down to line 28 where the appropriate number of days (31) is assigned to dayCount. The same idea is used in lines 30 35 for the months with 30 days. The tricky part, of course, is calculating the days in February of a particular year. The rules are as follows. If the year is a leap year, February contains 29 days; otherwise, it contains just 28 days. A year is a leap year if one of the following two conditions is true:

  • The year is divisible by 4 and not divisible by 100

  • The year is divisible by 400

These rules are implemented in lines 38 41. Recall how the modulus operator % allows the program to determine whether a number is divisible by another number. For example 400 % 4 is equal to 0 because the remainder is 0, meaning that 400 is divisible by 4.

Notice how easy it becomes to interpret the switch statement when combined with the enum type. There is no doubt as to what each switch section represents.

switch Statement Rule 4

The constant expression of each switch section must represent a value of the same type as, or a value implicitly convertible to, the governing type.

The following code snippet is invalid because there is no implicit conversion path specified from int to string:


graphics/08infig09.gif

whereas the next piece of source code is acceptable because int can be implicitly converted to long:


graphics/08infig10.gif

Working with switch Statements

The following are a few general guidelines when using switch statements.

Limit the Number of Statements in Each switch Section

Some switch statements might contain numerous switch sections. Many statements inside each switch section will clutter up the code and make it unclear. Instead, complicated actions requiring large numbers of statements can be collected inside methods and called from the switch section to reduce the number of statements here.

Order the Cases by Occurrence, by Number, or by Letter (Alphabetically)

The simple switch statements presented in this section merely included a few switch sections and could, therefore, be noted by a glimpse of the eye. However, some switch statements might contain large numbers of switch sections, leading to source code being exceedingly unclear and difficult to navigate. Introducing a scheme in which the case labels are ordered can improve the readability and save you and your fellow programmers precious time sifting through pages and pages of code.

There are several possible schemes available:

  • By occurrence If you position the switch sections you expect to be executed most frequently at the top of the of the switch section, you save other programmers from reading through code dealing with exceptional circumstances. You might also speed up the code by saving the computer from sifting through rarely processed case labels before finding the sought after switch section.

  • Alphabetically or by number To facilitate navigation through the different case labels, position them in alphabetical or numerical order.

Use the Default Section for the Same Purposes as the Default Statement of the Multibranch if-else Statements

Recall the two general rules mentioned during the multibranch if-else statement discussion about the default statement following the last else clause. They also apply to the default section of the switch statement and are just briefly summarized here:

  • Only use the default section for genuine defaults.

  • Detect errors with the default section if unused.

Because the discussion of these two recommendations in relation to the switch statement merely is a repeat of the discussion in the multibranch if-else statement section, please refer to that section for further details.


   


C# Primer Plus
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2000
Pages: 286
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net