Chapter 4
Flow Control

Wrox.com Code Downloads for this Chapter

You can find the wrox.com code downloads for this chapter at www.wrox.com/go/beginningvisualc#2015programming on the Download Code tab. The code is in the Chapter 4 download and individually named according to the names throughout the chapter.

All of the C# code you've seen so far has had one thing in common. In each case, program execution has proceeded from one line to the next in top-to-bottom order, missing nothing. If all applications worked like this, then you would be very limited in what you could do. This chapter describes two methods for controlling program flow — that is, the order of execution of lines of C# code: branching and looping. Branching executes code conditionally, depending on the outcome of an evaluation, such as “Execute this code only if the variable myVal is less than 10.” Looping repeatedly executes the same statements, either a certain number of times or until a test condition has been reached.

Both of these techniques involve the use of Boolean logic. In the last chapter, you saw the bool type, but didn't actually do much with it. In this chapter, you'll use it a lot, so the chapter begins by discussing what is meant by Boolean logic, and then goes on to cover how you can use it in flow control scenarios.

Boolean Logic

The bool type introduced in the previous chapter can hold one of only two values: true or false. This type is often used to record the result of some operation, so that you can act on this result. In particular, bool types are used to store the result of a comparison.

For instance, consider the situation (mentioned in the chapter introduction) in which you want to execute code based on whether a variable, myVal, is less than 10. To do this, you need some indication of whether the statement “myVal is less than 10” is true or false — that is, you need to know the Boolean result of a comparison.

Boolean comparisons require the use of Boolean comparison operators (also known as relational operators), which are shown in Table 4.1.

Table 4.1 Boolean Comparison Operators

Operator Category Example Expression Result
== Binary var1 = var2 == var3; var1 is assigned the value true if var2 is equal to var3, or false otherwise.
!= Binary var1 = var2 != var3; var1 is assigned the value true if var2 is not equal to var3, or false otherwise.
< Binary var1 = var2 < var3; var1 is assigned the value true if var2 is less than var3, or false otherwise.
> Binary var1 = var2 > var3; var1 is assigned the value true if var2 is greater than var3, or false otherwise.
<= Binary var1 = var2 <= var3; var1 is assigned the value true if var2 is less than or equal to var3, or false otherwise.
>= Binary var1 = var2 >= var3; var1 is assigned the value true if var2 is greater than or equal to var3, or false otherwise.

In all cases in Table 4.1, var1 is a bool type variable, whereas the types of var2 and var3 may vary.

You might use operators such as these on numeric values in code:

bool isLessThan10;
isLessThan10 = myVal < 10;

The preceding code results in isLessThan10 being assigned the value true if myVal stores a value less than 10, or false otherwise.

You can also use these comparison operators on other types, such as strings:

bool isBenjamin;
isBenjamin = myString == "Benjamin";

Here, isBenjamin is true only if myString stores the string "Benjamin".

You can also compare variables with Boolean values:

bool isTrue;
isTrue = myBool == true;

Here, however, you are limited to the use of the == and != operators.

The & and | operators also have two similar operators, known as conditional Boolean operators, shown in Table 4.2.

Table 4.2 Conditional Boolean Operators

Operator Category Example Expression Result
&& Binary var1 = var2 && var3; var1 is assigned the value true if var2 and var3 are both true, or false otherwise. (Logical AND)
|| Binary var1 = var2 || var3; var1 is assigned the value true if either var2 or var3 (or both) is true, or false otherwise. (Logical OR)

The result of these operators is exactly the same as & and |, but there is an important difference in the way this result is obtained, which can result in better performance. Both of these look at the value of their first operands (var2 in Table 4.2) and, based on the value of this operand, may not need to process the second operands (var3 in Table 4.2) at all.

If the value of the first operand of the && operator is false, then there is no need to consider the value of the second operand, because the result will be false regardless. Similarly, the || operator returns true if its first operand is true, regardless of the value of the second operand.

Boolean Bitwise and Assignment Operators

Boolean comparisons can be combined with assignments by combining Boolean bitwise and assignment operators. These work in the same way as the mathematical assignment operators that were introduced in the preceding chapter (+=, *=, and so on). The Boolean versions are shown in Table 4.3. When expressions use both the assignment (=) and bitwise operators (&, |, and ^), the binary representation of the compared quantities are used to compute the outcome, instead of the integer, string, or similar values.

Table 4.3 Boolean Assignment Operators

Operator Category Example Expression Result
&= Binary var1 &= var2; var1 is assigned the value that is the result of var1 & var2.
|= Binary var1 |= var2; var1 is assigned the value that is the result of var1 | var2.
^= Binary var1 ^= var2; var1 is assigned the value that is the result of var1 ^ var2.

For example, the equation var1 ^= var2 is similar to var1 = var1 ^ var2 where var1 = true and var2 = false. When comparing the binary representation of false which is 0000 to true, which is typically anything other than 0000 (usually 0001), var1 is set to true.

In the Try It Out that follows, you type in an integer and then the code performs various Boolean evaluations using that integer.

Operator Precedence Updated

Now that you have a few more operators to consider, Table 3-10: “Operator Precedence” from the previous chapter should be updated to include them. The new order is shown in Table 4.4.

Table 4.4 Operator Precedence (Updated)

Precedence Operators
Highest ++, −− (used as prefixes); (), +, (unary), !, ˜
*, /, %
+,
1, 1
<, >, <=, >=
==, !=
&
^
|
&&
||
=, *=, /=, %=, +=, −=, 1=, 1=, &=, ^=, |=
Lowest ++, –– (used as suffixes)

This adds quite a few more levels but explicitly defines how expressions such as the following will be evaluated, where the && operator is processed after the <= and >= operators (in this code var2 is an int value):

var1 = var2 <= 4 && var2 >= 2;

It doesn't hurt to add parentheses to make expressions such as this one clearer. The compiler knows what order to process operators in, but we humans are prone to forget such things (and you might want to change the order). Writing the previous expression as

var1 = (var2 <= 4) && (var2 >= 2);

solves this problem by explicitly ordering the computation.

Branching

Branching is the act of controlling which line of code should be executed next. The line to jump to is controlled by some kind of conditional statement. This conditional statement is based on a comparison between a test value and one or more possible values using Boolean logic.

This section describes three branching techniques available in C#:

  • The ternary operator
  • The if statement
  • The switch statement

The Ternary Operator

The simplest way to perform a comparison is to use the ternary (or conditional) operator mentioned in the last chapter. You've already seen unary operators that work on one operand, and binary operators that work on two operands, so it won't come as a surprise that this operator works on three operands. The syntax is as follows:

<test> ? <resultIfTrue>: <resultIfFalse>

Here, <test> is evaluated to obtain a Boolean value, and the result of the operator is either <resultIfTrue> or <resultIfFalse> based on this value.

You might use this as follows to test the value of an int variable called myInteger:

string resultString = (myInteger < 10) ? "Less than 10"
                                  : "Greater than or equal to 10";

The result of the ternary operator is one of two strings, both of which may be assigned to resultString. The choice of which string to assign is made by comparing the value of myInteger to 10. In this case, a value of less than 10 results in the first string being assigned, and a value of greater than or equal to 10 results in the second string being assigned. For example, if myInteger is 4, then resultString will be assigned the string Less than 10.

The if Statement

The if statement is a far more versatile and useful way to make decisions. Unlike ?: statements, if statements don't have a result (so you can't use them in assignments); instead, you use the statement to conditionally execute other statements.

The simplest use of an if statement is as follows, where <test> is evaluated (it must evaluate to a Boolean value for the code to compile) and the line of code that follows the statement is executed if <test> evaluates to true:

if (<test>)
   <code executed if <test> is true>;

After this code is executed, or if it isn't executed due to <test> evaluating to false, program execution resumes at the next line of code.

You can also specify additional code using the else statement in combination with an if statement. This statement is executed if <test> evaluates to false:

if (<test>)
   <code executed if <test> is true>;
else
   <code executed if <test> is false>;

Both sections of code can span multiple lines using blocks in braces:

if (<test>)
{
   <code executed if <test> is true>;
}
else
{
   <code executed if <test> is false>;
}

As a quick example, you could rewrite the code from the last section that used the ternary operator:

string resultString = (myInteger < 10) ? "Less than 10"
                                  : "Greater than or equal to 10";

Because the result of the if statement cannot be assigned to a variable, you have to assign a value to the variable in a separate step:

string resultString;
if (myInteger < 10)
   resultString = "Less than 10";
else
   resultString = "Greater than or equal to 10";

Code such as this, although more verbose, is far easier to read and understand than the equivalent ternary form, and enables far more flexibility.

The following Try It Out illustrates the use of the if statement.

Checking More Conditions Using if Statements

In the preceding example, you checked for three conditions involving the value of var1. This covered all possible values for this variable. Sometimes, you might want to check for specific values — for example, if var1 is equal to 1, 2, 3, or 4, and so on. Using code such as the preceding can result in annoyingly nested code:

if (var1 == 1)
{
   // Do something.
}
else
{
   if (var1 == 2)
   {
      // Do something else.
   }
   else
   {
      if (var1 == 3 || var1 == 4)
      {
         // Do something else.
      }
      else
      {
         // Do something else.
      }
   }
}

In these situations, consider using a slightly different indentation scheme and contracting the section of code for the else blocks (that is, using a single line of code after the else blocks, rather than a block of code). That way, you end up with a structure involving else if statements:

if (var1 == 1)
{
   // Do something.
}
else if (var1 == 2)
{
   // Do something else.
}
else if (var1 == 3 || var1 == 4)
{
   // Do something else.
}
else
{
   // Do something else.
}

These else if statements are really two separate statements, and the code is functionally identical to the previous code, but much easier to read. When making multiple comparisons such as this, consider using the switch statement as an alternative branching structure.

The switch Statement

The switch statement is similar to the if statement in that it executes code conditionally based on the value of a test. However, switch enables you to test for multiple values of a test variable in one go, rather than just a single condition. This test is limited to discrete values, rather than clauses such as “greater than X,” so its use is slightly different; however, it can be a powerful technique.

The basic structure of a switch statement is as follows:

switch (<testVar>)
{
   case <comparisonVal1>:
      <code to execute if <testVar> == <comparisonVal1> >
      break;
   case <comparisonVal2>:
      <code to execute if <testVar> == <comparisonVal2> >
      break;
   …
   case <comparisonValN>:
      <code to execute if <testVar> == <comparisonValN> >
      break;
   default:
      <code to execute if <testVar> != comparisonVals>
      break;
}

The value in <testVar> is compared to each of the <comparisonValX> values (specified with case statements). If there is a match, then the code supplied for this match is executed. If there is no match, then the code in the default section is executed if this block exists.

On completion of the code in each section, you have an additional command, break. It is illegal for the flow of execution to reach a second case statement after processing one case block.

The break statement here simply terminates the switch statement, and processing continues on the statement following the structure.

There are alternative methods for preventing flow from one case statement to the next in C# code. You can use the return statement, which results in termination of the current function, rather than just the switch structure (see Chapter 6 for more details about this), or a goto statement. goto statements (as detailed earlier) work here because case statements actually define labels in C# code. Here is an example:

switch (<testVar>)
{
   case <comparisonVal1>:
      <code to execute if <testVar> == <comparisonVal1> >
      goto case <comparisonVal2>;
   case <comparisonVal2>:
      <code to execute if <testVar> == <comparisonVal2> >
      break;
   …

Here's one exception to the rule that the processing of one case statement can't run freely into the next: If you place multiple case statements together (stack them) before a single block of code, then you are in effect checking for multiple conditions at once. If any of these conditions is met, then the code is executed. Here's an example:

switch (<testVar>)
{
   case <comparisonVal1>:
   case <comparisonVal2>:
      <code to execute if <testVar> == <comparisonVal1> or
                          <testVar> == <comparisonVal2> >
      break;
   …

These conditions also apply to the default statement. There is no rule stipulating that this statement must be the last in the list of comparisons, and you can stack it with case statements if you want. Adding a breakpoint with break, or return, ensures that a valid execution path exists through the structure in all cases.

The following Try It Out uses a switch statement to write different strings to the console, depending on the value you enter for a test string.

while Loops

while loops are very similar to do loops, but they have one important difference: The Boolean test in a while loop takes place at the start of the loop cycle, not at the end. If the test evaluates to false, then the loop cycle is never executed. Instead, program execution jumps straight to the code following the loop.

Here's how while loops are specified:

while (<Test>)
{
   <code to be looped>
}

They can be used in almost the same way as do loops:

int i = 1;
while (i <= 10)
{
   WriteLine($"{i++}");
}

This code has the same result as the do loop shown earlier; it outputs the numbers 1 to 10 in a column. The following Try It Out demonstrates how you can modify the last example to use a while loop.

for Loops

The last type of loop to look at in this chapter is the for loop. This type of loop executes a set number of times and maintains its own counter. To define a for loop you need the following information:

  • A starting value to initialize the counter variable
  • A condition for continuing the loop, involving the counter variable
  • An operation to perform on the counter variable at the end of each loop cycle

For example, if you want a loop with a counter that increments from 1 to 10 in steps of one, then the starting value is 1; the condition is that the counter is less than or equal to 10; and the operation to perform at the end of each cycle is to add 1 to the counter.

This information must be placed into the structure of a for loop as follows:

for (<initialization>; <condition>; <operation>)
{
   <code to loop>
}

This works exactly the same way as the following while loop:

<initialization>
while (<condition>)
{
   <code to loop>
   <operation>
}

Earlier, you used do and while loops to write out the numbers from 1 to 10. The code that follows shows what is required to do this using a for loop:

int i;
for (i = 1; i <= 10; ++i)
{
   WriteLine($"{i}");
}

The counter variable, an integer called i, starts with a value of 1 and is incremented by 1 at the end of each cycle. During each cycle, the value of i is written to the console.

When the code resumes after the loop, i has a value of 11. That's because at the end of the cycle where i is equal to 10, i is incremented to 11. This happens before the condition i <= 10 is processed, at which point the loop ends. As with while loops, for loops execute only if the condition evaluates to true before the first cycle, so the code in the loop doesn't necessarily run at all.

As a final note, you can declare the counter variable as part of the for statement, rewriting the preceding code as follows:

for (int i = 1; i <= 10; ++i)
{
   WriteLine($"{i}");
}

If you do this, though, the variable i won't be accessible from code outside this loop (see the “Variable Scope” section in Chapter 6).

Interrupting Loops

Sometimes you want finer-grained control over the processing of looping code. C# provides commands to help you here:

  • break — Causes the loop to end immediately
  • continue — Causes the current loop cycle to end immediately (execution continues with the next loop cycle)
  • return — Jumps out of the loop and its containing function (see Chapter 6)

The break command simply exits the loop, and execution continues at the first line of code after the loop, as shown in the following example:

int i = 1;
while (i <= 10)
{
   if (i == 6)
      break;
   WriteLine($"{i++}");
}

This code writes out the numbers from 1 to 5 because the break command causes the loop to exit when i reaches 6.

continue only stops the current cycle, not the whole loop, as shown here:

int i;
for (i = 1; i <= 10; i++)
{
   if ((i % 2) == 0)
      continue;
   WriteLine(i);
}

In the preceding example, whenever the remainder of i divided by 2 is zero, the continue statement stops the execution of the current cycle, so only the numbers 1, 3, 5, 7, and 9 are displayed.

Infinite Loops

It is possible, through both coding errors and design, to define loops that never end, so-called infinite loops. As a very simple example, consider the following:

while (true)
{
   // code in loop
}

This can be useful, and you can always exit such loops using code such as break statements or manually by using the Windows Task Manager. However, when this occurs by accident, it can be annoying. Consider the following loop, which is similar to the for loop in the previous section:

int i = 1;
while (i = 10)
{
   if ((i % 2) == 0)
      continue;
   WriteLine($"{i++}");
}

Here, i isn't incremented until the last line of code in the loop, which occurs after the continue statement. If this continue statement is reached (which it will be when i is 2), the next loop cycle will be using the same value of i, continuing the loop, testing the same value of i, continuing the loop, and so on. This will cause the application to freeze. Note that it's still possible to quit the frozen application in the normal way, so you won't have to reboot if this happens.

image What You Learned in This Chapter

Topic Key Concepts
Boolean logic Boolean logic involves using Boolean (true or false) values to evaluate conditions. Boolean operators are used to perform comparisons between values and return Boolean results. Some Boolean operators are also used to perform bitwise operations on the underlying bit structure of values, and there are some specialized bitwise operators too.
Branching You can use Boolean logic to control program flow. The result of an expression that evaluates to a Boolean value can be used to determine whether a block of code is executed. You do this with if statements or the ?: (ternary) operator for simple branching, or the switch statement to check multiple conditions simultaneously.
Looping Looping allows you to execute blocks of code a number of times according to conditions you specify. You can use do and while loops to execute code while a Boolean expression evaluates to true, and for loops to include a counter in your looping code. Loops can be interrupted by cycle (with continue) or completely (with break). Some loops end only if you interrupt them; these are called infinite loops.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.182.97