Using Enumerations and Structures - Program Statements - C# 24-Hour Trainer (2015)

C# 24-Hour Trainer (2015)

Section III

Program Statements

The lessons in Section II focused on working with variables. They explained how to declare variables, set their values, and perform calculations.

Those techniques let you do some fairly complex things, but they're still relatively straightforward things that you could do yourself by hand if you really had to. For example, you could easily calculate line item totals, sales tax, shipping, and a grand total for a purchase order.

With what you know so far, you really can't write a program that takes full advantage of the computer's power. You can't make the program add up an unknown number of values stored in a ListBox, perform the same task (such as calculating an account balance) for thousands of customers, or take different actions depending on the user's inputs. You can't even write a program that can tell if the user entered an invalid value such as “seventy-eight” in a TextBox that should contain a number.

The lessons in this section explain how to perform these kinds of tasks. They explain ways you can make a program take different courses of action depending on circumstances, repeat a set of actions many times, break code into manageable pieces to make it easier to write and debug, and handle unexpected errors. After you finish reading these lessons, you'll be able to write applications that are much more powerful than those you can write now.

· Lesson 18: Making Choices

· Lesson 19: Repeating Program Steps

· Lesson 20: Reusing Code with Methods

· Lesson 21: Handling Errors

· Lesson 22: Preventing Bugs

Lesson 18

Making Choices

All of the code used in the lessons so far has been completely linear. The program follows a series of steps in order with no deviation.

For example, a sales program could multiply a unit price by quantity desired, add several items’ values, multiply to get sales tax and shipping costs, and calculate a grand total.

So far there’s been no way to perform different steps under different circumstances. For example, the sales program couldn’t charge different prices for different quantities purchased or waive shipping charges for orders over $100. It couldn’t even check quantities to see if they make sense. So far a clever customer could order –1,000 items to get a huge credit!

In this lesson you learn how a program can make decisions. You learn how the program can take different actions based on user inputs and other circumstances.

Decision Statements

Programs often need to decide between two or more courses of action. For example:

· If it’s before 4:00 p.m., ship today. Otherwise ship tomorrow.

· If the order quantity is less than zero, make the user fix it.

· If a word processor has unsaved changes, refuse to exit.

· Calculate shipping based on order total: $5 if total < $20, $7.50 if total < $50, $10 if total < $75, and free if total ≥ $75.

The basic idea is the same in all of these cases. The program examines a value and takes one of several different actions depending on the value.

The following sections describe the different statements that C# provides for making these kinds of decisions.

if Statements

The if statement examines a condition and takes action only if the condition is true. The basic syntax for the if statement is:

if (condition) statement;

Here condition is some boolean expression that evaluates to either true or false, and statement is a statement that should be executed if condition is true.

For example, suppose you’re writing an order entry program and shipping should be $5 for orders under $100 and free for orders of at least $100. Suppose also that the program has already calculated the value total. The following code shows how the program might handle this:

decimal shipping = 5.00M; // Default shipping cost.

if (total >= 100) shipping = 0; // Shipping is free if total >= 100.

The code starts by setting the variable shipping to $5. Then if the previously calculated value total is at least $100, the program sets shipping to $0.

If total is less than $100, the statement following the if statement is not executed and shipping keeps its original value of $5.

If you want to execute more than one statement when condition is true, place the statements inside braces as in the following code:

decimal shipping = 5.00M; // Default shipping cost.

if (total >= 100)

{

shipping = 0; // Shipping is free if total >= 100.

giveFreeGift = true; // Give a free gift if total >= 100.

}

You can place as many statements as you like inside the braces, and they are all executed if condition is true.

NOTE

To make the code more consistent and easier to read, some programmers always use braces even if the program should execute only one statement. The following code shows an example:

if (total >= 100)

{

shipping = 0;

}

Other programmers think that’s unnecessarily verbose. You should use the style you find easiest to read.

if-else Statements

The previous example set shipping to a default value and then changed it if total was at least $100. Another way to think about this problem is to imagine taking one of two actions depending on total’s value. If total is less than $100, the program should set shippingto $5. Otherwise the program should set shipping to $0.

The if-else construct lets a program follow this approach, taking one of two actions depending on some condition.

The syntax for if-else is:

if (condition)

statementsIfTrue;

else

statementsIfFalse;

If condition is true, the first block statementsIfTrue executes. Otherwise (if condition is false) the second block statementsIfFalse executes.

Using the else keyword, the preceding code could be rewritten like this:

decimal shipping;

if (total < 100)

shipping = 5M; // Shipping is $5 if total < 100.

else

shipping = 0M; // Shipping is free if total >= 100.

You can use braces to make either the if or else part of the if-else statement execute more than one command.

Cascading if Statements

The if-else construct performs one of two actions depending on whether the condition is true or false. Sometimes a program needs to check several conditions to decide what to do.

For example, suppose an order entry program calculates shipping charges depending on the total purchase amount according to this schedule:

· If total < $20, shipping is $5.00.

· Otherwise, if total < $50, shipping is $7.50.

· Otherwise, if total < $75, shipping is $10.00.

· Otherwise, shipping is free.

You can make a program perform each of these tests one after another by making a second if statement be the else part of a first if statement. The following code shows how you can calculate shipping according to the preceding schedule:

decimal shipping;

if (total < 20)

{

shipping = 5M;

}

else if (total < 50)

{

shipping = 7.5M;

}

else if (total < 75)

{

shipping = 10M;

}

else

{

shipping = 0M;

}

When the program encounters a cascading series of if statements, it executes each in turn until it finds one with a true condition. It then skips the rest because they are all part of the current if statement’s else block.

For example, consider the previous code and suppose total is $60. The code evaluates the first condition and decides that (total < 20) is false, so it does not execute the first code block.

The program skips to the else statement and executes the next if test. The program decides that (total < 50) is also not true, so it skips to this if statement’s else block.

The program executes the third if test and finds that (total < 75) is true so it executes the statement shipping = 10M.

Because the program found an if statement with a true condition, it skips the following else statement, so it passes over any if statements that follow without evaluating their conditions.

Nested if Statements

Another common arrangement of if statements nests one within another. The inner if statement is executed only if the first statement’s condition allows the program to reach it.

For example, suppose you charge customers 5 percent state sales tax. If a customer lives within your county, you also charge a county transportation tax. Finally, if the customer also lives within city limits, you charge a city sales tax. (Taxes where I live are at least this confusing.)

The following code performs these checks, where the variables inCounty and inCity indicate whether the customer lives within the county and city:

if (inCounty)

{

if (inCity)

{

salesTaxRate = 0.09M;

}

else

{

salesTaxRate = 0.07M;

}

}

else

{

salesTaxRate = 0.05M;

}

You can nest if statements as deeply as you like, although at some point the code gets hard to read.

NOTE

There are always ways to rearrange code by using the && (logical AND) and || (logical OR) operators to remove nested if statements. For example, the following code does the same thing as the previous version without nesting:

if (inCounty && inCity)

{

salesTaxRate = 0.09M;

}

else if (inCounty)

{

salesTaxRate = 0.07M;

}

else

{

salesTaxRate = 0.05M;

}

In fact, if you know that the city lies completely within the county, you could rewrite the first test as if (inCity).

Switch Statements

The switch statement provides an easy-to-read equivalent to a series of cascading if statements that compares one value to a series of other values.

The syntax of the switch statement is:

switch (testValue)

{

case (value1):

statements1;

break;

case (value2):

statements2;

break;

default:

statementsDefault;

break;

}

Here testValue is the value that you are testing. The values value1, value2, and so on are the values to which you are comparing testValue. The statements1, statements2, and so on are the blocks of statements that you want to execute for each case. The other pieces (switch, case, break, and default) are keywords that you must type as they appear here.

If you include the optional default section, its statements execute if no other case applies. Actually the case statements are optional, too, although it would be strange to not use any.

Note that a case’s code block doesn’t need to include any statements other than break. You can use that to make the code take no action when a particular case occurs.

For example, suppose you build a form where the user selects a hotel from a ComboBox. The program uses that selection to initialize an enumerated variable named hotelChoice. The following code sets the lodgingPrice variable depending on which hotel the user selected:

decimal lodgingPrice;

switch (hotelChoice)

{

case HotelChoice.LuxuryLodge:

lodgingPrice = 45;

break;

case HotelChoice.HamiltonArms:

lodgingPrice = 80;

break;

case HotelChoice.InvernessInn:

lodgingPrice = 165;

break;

default:

MessageBox.Show("Please select a hotel");

lodgingPrice = 0;

break;

}

The case statements check for the three expected choices and sets lodgingPrice to the appropriate value. If the user doesn’t select any hotel, the default section’s code displays a message box and sets lodgingPrice to 0 to indicate a problem.

A switch statement is most robust (less prone to bugs and crashes) if its cases can handle every possible comparison value. That makes them work very well with enumerated types because you can list every possible value. In contrast, you can’t include a casestatement for every possible integer value (unless you include several billion lines of code), so case statements can’t check every possible integer value.

Even if the case statements check every possible value in an enumeration, it’s a good practice to include a default section just in case another value sneaks into the code. For example, a bug in the code could convert an integer into an enumeration value that doesn’t exist, or you could later add a new value to the enumeration and forget to add a corresponding case statement. In those cases, the default statement can catch the bug, take some default action, and possibly warn you that something is wrong.

When you use a switch statement with other data types, be sure to consider unexpected values, particularly if the user typed in the value. For example, don’t assume the user will always enter a valid string. Allowing the user to select a string from a ComboBox is safer, but you should still include a default statement.

Try It

In this Try It, you build the Order Form program shown in Figure 18.1. The program uses a cascading series of if statements to calculate shipping cost based on the subtotal.

Screenshot of Order Form window with Item, Price Each, Quantity, and Price columns; Subtotal, Sales Tax, Shipping, and Grand Total textboxes under Price column; and Calculate button.

Figure 18.1

Lesson Requirements

In this lesson, you:

· Build the form shown in Figure 18.1.

· Write the code for the Calculate button so it calculates the subtotal, sales tax, shipping, and grand total. The sales tax should be 7 percent of the subtotal. Shipping should be $5 if subtotal < $20, $7.50 if subtotal < $50, $10 if subtotal < $75, and free if subtotal ≥ $75.

NOTE

You can download the code and resources for this lesson from the website at www.wrox.com/go/csharp24hourtrainer2e.

Hints

· Make the sales tax rate a constant, giving it the most limited scope you can.

Step-by-Step

· Build the form shown in Figure 18.1.

1. This is relatively straightforward.

· Write the code for the Calculate button so it calculates the subtotal, sales tax, shipping, and grand total. The sales tax should be 7 percent of the subtotal. Shipping should be $5 if subtotal < $20, $7.50 if subtotal < $50, $10 if subtotal < $75, and free if subtotal ≥ $75.

1. Calculate the total costs for each of the four items. Add them together to get the subtotal.

2. Calculate sales tax by multiplying the tax rate by the subtotal.

3. Use a series of cascading if-else statements to calculate the shipping cost based on the subtotal as in the following code:

4. // Calculate shipping cost.

5. decimal shipping;

6. if (subtotal < 20)

7. {

8. shipping = 5;

9. }

10. else if (subtotal < 50)

11. {

12. shipping = 7.5m;

13. }

14. else if (subtotal < 75)

15. {

16. shipping = 10;

17. }

18. else

19. {

20. shipping = 0;

}

21.Add the subtotal, tax, and shipping cost to get the grand total.

22.Display the results.

Exercises

1. 1 Build the Conference Coster program shown in Figure 18.2.The Conference Coster window displaying three list boxes, Transportation to Conference, Hotel, and Local Transportation, with a selected item for each box; the Calculate button; and Grand Total textbox.

Figure 18.2

When the user clicks the Calculate button, first check each ListBox’s SelectedIndex property. If any SelectedIndex is less than zero (indicating the user didn’t make a choice), display an error message and use the return keyword to stop calculating.

If the user made a choice in all of the ListBoxes, create a variable total to hold the total cost. Use three switch statements to add the appropriate amounts to total and display the result. (Hint: Add a default statement to each switch statement to catch unexpected selections, even though none should occur in this program. Then add a new hotel to the ListBox and see what happens if you select it.)

2. [SimpleEdit, Hard] Copy the SimpleEdit program that you built way back in Exercise 8-7 (or download the version on the book’s website) and add code to protect the user from losing unsaved changes.

The basic idea is to check whether the document has been modified before doing anything that will lose the changes, such as starting a new document, opening another file, or exiting the program.

a. In the File menu’s New, Open, and Exit commands, check the RichTextBox’s Modified property to see if the document has unsaved changes.

b. If there are unsaved changes, ask if the user wants to save them. Display a message box with the buttons Yes, No, and Cancel.

c. If the user clicks Yes, save the changes and continue the operation.

d. If the user clicks No, don’t save the changes (do nothing special) and let the operation continue.

e. If the user clicks Cancel, don’t perform the operation. For example, don’t open a new file.

f. After starting a new document or saving an old one, set the RichTextBox control’s Modified property to false to indicate that there are no unsaved changes at that time.

Hint: Use a local variable named shouldContinue to decide whether the operation should continue.

3. [SimpleEdit] Copy the SimpleEdit program you built for Exercise 2. That program protects against lost changes if the user opens the File menu and selects Exit, but the user can close the program several other ways such as pressing Alt+F4, clicking the “X” button in the program’s title bar, and opening the system menu in the form’s upper-left corner and selecting Close. Currently the program doesn’t protect unsaved changes for any of those.

To fix this, give the form a FormClosing event handler. When the form is about to close, it raises this event. If you set the event’s e.Cancel parameter to true, the form cancels the close and remains open. Add code to this event handler to protect unsaved changes.

Now that the FormClosing event handler is protecting against lost changes, you don’t need to perform the same checks in the Exit menu item’s event handler. Make that event handler simply call the Close method and let the FormClosing event handler do the rest.

4. [Games, Hard] Copy the tic-tac-toe program that you built way back in Exercise 16-8 (or download the version on the book’s website). That version of the program uses three Labels for each square: two to let the user select the square for X or O, and one to show which player has taken the square.

Modify the program to make the following changes (which should make the program much smaller):

· Remove the X and O Labels so there’s only one Label per square.

· Set each Label’s Tag property to indicate its row and column. For example, set the Tag property for the upper-left Label to “0, 0.”

· Make a class-level variable to keep track of which player’s turn it is.

· Use the same Click event handler for all of the Labels.

· When the user clicks a square, convert the event handler’s sender parameter into the Label that raised the Click event.

· If the square has already been taken, ignore the click.

· Otherwise, take the square for the player whose turn it is.

· Parse the clicked Label’s Tag property to see which entry in the Board array to set. (Hint: Use ToString to convert the Tag property into a string.)

5. [Games] Copy the program you made for Exercise 4 and modify it so that when the last square is taken, the program says “All squares are taken” in the turn Label (instead of saying “O’s turn”).

6. [Games, Hard] Copy the program you made for Exercise 5 and modify it so it checks for a winner after each square is taken. When the game ends, display the winner (or the fact that it’s a tie) in the turn Label. After the game is over, ignore any click events until the user starts a new game.

7. [Games, Hard] Make a program that displays a bouncing ball (shown in a PictureBox). When the program starts, give the PictureBox a random position on the form and random X and Y velocities. When a Timer ticks, use the velocities to calculate the PictureBox’s new position. If the position makes the PictureBox move beyond one of the form’s edges, move it back onto the form and reverse the corresponding velocity.

8. [Games, Hard] Copy the program you made for Exercise 7 and add a sound effect by following these steps:

· In the Solution Explorer, double-click the Properties item. Select the References tab, open the Add Resource dropdown, and select Add Existing File. Select the sound effect’s source file and click Open.

· Use class-level code similar to the following to create a sound player associated with the sound resource. (Here “boing” is the name of the resource I used.)

· // The SoundPlayer.

· private System.Media.SoundPlayer BoingSound =

new System.Media.SoundPlayer(Properties.Resources.boing);

· Use the statement BoingSound.Play() to play the sound when necessary.

9. [Games, Hard] Copy the program you made for Exercise 11-17 (or download the version on the book’s website) and make the following changes:

· Remove the Stop button.

· When the user clicks Fire, play a sound file that sounds like a cannon firing.

· Place a new PictureBox displaying a picture of a castle or some other target on the form.

· If the cannonball hits the target, stop moving it, play an explosion sound file, hide the cannonball, and make the target PictureBox display an image of an explosion.

· If the cannonball moves off of the form, stop moving it and play a failure sound effect.

10.[Games, Hard] Make a UFO shooting gallery game similar to the one shown in Figure 18.3.Screenshot of Ufo window depicting a UFO shooting gallery game with an image of the Ufo (top), an inverted T (bottom), and a rectangular object (middle). The Score is displayed at the upper left corner of the window.

Figure 18.3

· Make the image of a UFO move left-to-right across the top of the form. When the UFO leaves the right side of the form, make it reappear on the left side.

· When the user presses Space, fire the red laser bolt (a PictureBox) from the laser cannon (an image in another PictureBox). (Hint: To know when the user presses Space, catch the form’s KeyDown event and see if e.KeyCode == Keys.Space.)

· Don’t allow the user to fire a bolt if one is already on the form.

· Use variables UfoX, UfoY, UfoVx, and UfoVy to track the UFO’s position and velocity. Use similar variables for the laser bolt.

· When a Timer fires, update the positions of the UFO and the bolt.

· Use a variable to keep track of hit count.

· If the bolt hits the UFO, hide the bolt, increment the hit count, and display the hit count in the score Label at the top of the form.

· If the bolt leaves the form, hide it.

· Play cool sounds when the laser cannon fires and when a bolt hits the UFO.

11.[Games] The program you wrote for Exercise 10 isn’t very hard. After a minute or two, you can easily get the timing down and hit the UFO almost every time.

Copy that program and make it more challenging by making these changes.

· When you start the UFO at the left edge of the form, give it a random size, speed, and Y coordinate.

· When the user hits the UFO, award points that take into account the current size and speed.

12.[Games, Hard] The program you wrote for Exercise 11 is still fairly easy because the user has an unlimited amount of ammunition.

Copy the program and modify it so the user has only 10 laser bolts. Represent each with a PictureBox visible on the form and keep track of the number of bolts remaining. When the user fires a bolt, use a switch statement to hide the next bolt PictureBox.

When all of the bolts are used, display a label on top of the game that shows the user’s final score and play a triumphant fanfare.

13.[Games] Copy the program you wrote for Exercise 12 and add a File menu with New Game and Exit menu items.

14.[Games, Hard] Copy the program you wrote for Exercise 13 and add high scores to it.

· Use arrays to keep track of the five highest scores and the names of the players who got those high scores.

· Give the File menu a new High Scores command that displays the five names and scores in a dialog.

· When a game ends, compare the player’s score to the first item in the high scores array. If the new score is higher:

§ Display a form that lets the user enter a name.

§ Replace the first array entries with the new score and name.

§ Use Array.Sort to sort the arrays.

§ Display the high scores form.

NOTE

Please select the videos for Lesson 18 online at www.wrox.com/go/csharp24hourtrainer2evideos.