Using Variables and Performing Calculations - Variables and Calculations - C# 24-Hour Trainer (2015)

C# 24-Hour Trainer (2015)

Section II

Variables and Calculations

You may have noticed that the lessons up to this point haven't done much with numbers, dates, text (other than to just display it), or any other pieces of data. They've mostly dealt with controls and their properties, methods, and events.

Although you can do some fairly impressive things with controls alone, most programs also need to manipulate data. They need to do things like add purchasing costs, calculate sales tax, sort appointments by time, and search text for keywords.

The lessons in this section explain how to perform these kinds of tasks. They explain the variables that a program uses to represent data in code, and they show how to manipulate variables to calculate new results.

· Lesson 11: Using Variables and Performing Calculations

· Lesson 12: Debugging Code

· Lesson 13: Understanding Scope

· Lesson 14: Working with Strings

· Lesson 15: Working with Dates and Times

· Lesson 16: Using Arrays and Collections

· Lesson 17: Using Enumerations and Structures

Lesson 11

Using Variables and Performing Calculations

A variable holds a value in memory so a program can manipulate it. Different kinds of variables hold different types of data: numbers, text, LOL cat pictures, Halo scores, even complex groups of data such as employee records.

In this lesson you learn what variables are and how to use them. You learn how to define variables, put data in them, and use them to perform simple calculations.

What Are Variables?

Technically speaking a variable is a named piece of memory that can hold some data of a specific type. For example, a program might allocate 4 bytes of memory to store an integer. You might name those bytes “payoffs” so you can easily refer to them in the program’s code.

Less technically, you can think of a variable as a named place to put a piece of data. The program’s code can use the variables to store values and perform calculations. For example, a program might store two values in variables, add the values together, and store the result in a third variable.

Data Types

Every variable has a particular data type that determines the kind of data that it can hold. In general, you cannot place data of one type in a variable of another. For example, if price is a variable that can hold a number in dollars and cents, you cannot put the string “Hello” in it.

If you like, you can think of a variable as an envelope (with a name written on the outside) that can hold some data, but each type of data requires a different shaped envelope. Integers need relatively small envelopes, floats (which hold numbers with decimal points) need envelopes that are long and thin, and strings need big fat envelopes.

Bits and Bytes

A bit is a single binary digit of memory that can have the value 0 or 1. (The name “bit” comes from “BInary digiT.” Or is it “Binary digIT?”) Generally, bits are grouped into bytes and a program doesn’t work with bits directly.

A byte is a chunk of memory holding 8 bits. If you view the bits as digits in a binary number, then a byte can hold values between 0 (00000000 in binary) and 255 (11111111 in binary). Groups of bytes make up larger data types such as integers and strings.

A nibble is half a byte. Way back in the old days when memory was expensive and computers filled warehouses instead of laps, some programs needed to split bytes and consider the nibbles separately to save space. Now that memory is as cheap as day-old lottery tickets, the nibble is a historical curiosity mostly useful for impressing your friends at parties.

Bigger units of memory include kilobyte (KB) = 1,024 bytes, megabyte (MB) = 1,024KB, gigabyte (GB) = 1,024MB, and terabyte (TB) = 1,024GB. These are often used to measure the size of files, computer memory, flash drives, and disk drives. (Although in some contexts people use powers of 1,000 instead of 1,024. For example, most disk drive manufacturers define a gigabyte as 1,000,000,000 bytes, which in a sense shortchanges you out of 70MB.)

Sometimes the line between two data types is a bit fuzzy. For example, if a variable should hold a number, you cannot put in the string “ten.” The fact that “ten” is a number is obvious to a human but not to a C# program.

You can’t even place a string containing the characters “10” in a variable that holds a number. Though it should be obvious to just about anyone that “10” is a number, C# just knows it’s a string containing two characters “1” and “0” and doesn’t try to determine that the characters in the string represent a number.

Programs often need to convert a value from one data type to another (particularly switching between strings and numbers), so C# provides an assortment of data-conversion functions to do just that. The section “Type Conversions” later in this lesson describes those functions.

Table 11.1 summarizes C#’s built-in data types. The signed types can store values that are positive or negative, and the unsigned types can hold only positive values.

Table 11.1

Data Type

Meaning

Range

byte

Byte

0 to 255

sbyte

Signed byte

–128 to 127

short

Small signed integer

–32,768 to 32,767

ushort

Unsigned short integer

0 to 65,535

int

Integer

–2,147,483,648 to 2,147,483,647

uint

Unsigned integer

0 to 4,294,967,295

long

Long integer

–9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

ulong

Unsigned long integer

0 to 18,446,744,073,709,551,615

float

Floating point

Roughly –3.4e38 to 3.4e38

double

Double-precision floating point

Roughly –1.8e308 to 1.8e308

decimal

Higher precision and smaller range than floating-point types

See the following section, “Float, Double, and Decimal Data Types.”

char

Character

A single Unicode character. (Unicode characters use 16 bits to hold data for text in scripts such as Arabic, Cyrillic, Greek, and Thai, in addition to the Roman alphabet.)

string

Text

A string of Unicode characters.

bool

Boolean

Can be true or false.

object

An object

Can point to almost anything.

Some of these data types are a bit confusing but the most common data types (int, long, float, double, and string) are fairly straightforward, and they are the focus of most of this lesson. Before moving on to further details, however, it’s worth spending a little time comparing the float, double, and decimal data types.

Float, Double, and Decimal Data Types

The computer represents values of every type in binary using bits and bytes, so some values don’t fit perfectly in a particular data type. In particular, real numbers such as 1/7 don’t have exact binary representations, so the float, double, and decimal data types often introduce slight rounding errors.

For example, a float represents 1/7 as approximately 0.142857149. Usually the fact that this is not exactly 1/7 isn’t a problem, but once in a while if you compare two float values to see if they are exactly equal, roundoff errors make them appear different even though they should be the same.

The decimal data type helps reduce this problem for decimal values such as 1.5 (but not non-decimal real values such as 1/7) by storing an exact representation of a decimal value. Instead of storing a value as a binary number the way float and double do, decimal stores the number’s digits and its exponent separately as integral data types with no rounding. That lets it hold 28 or 29 significant digits (depending on the exact value) for numbers between roughly –7.9e28 and 7.9e28.

NOTE

The notation 7.9e28 means 7.9 × 1028.

Note that rounding errors can still occur when you combine decimal values. For example, if you add 1e28 plus 1e–28, the result would have more than the 28 or 29 significant digits that a decimal can provide so it rounds off to 1e28.

The moral of the story is that you should always use the decimal data type for values where you need great accuracy and the values won’t get truly enormous. In particular, you should always use decimal for currency values. Unless you’re Bill Gates’s much richer uncle, you’ll never get close to the largest value a decimal can represent, and the extra precision can prevent rounding errors during some fairly complex calculations.

NOTE

Another interesting feature of the decimal type is that, because of the way it stores its significant digits, it remembers zeros on the right. For example, if you add the values 1.35 and 1.65 as floats, you get the value 3. In contrast, if you add the same values as decimals, you get 3.00. The decimal result remembers that you were working with two digits to the right of the decimal point so it stores the result that way, too.

Declaring Variables

To declare a variable in C# code, give the data type that you want to use followed by the name that you want to give the variable. For example, the following code creates a variable named numMistakes. The variable’s data type is int so it can hold an integer between –2,147,483,648 and 2,147,483,647 (which should be enough for most projects that don’t involve the government):

int numMistakes;

You can use the equals symbol to assign a value to a variable. For example, the following code sets numMistakes to 1337:

numMistakes = 1337;

As an added convenience, you can declare a variable and give it a value at the same time, as in:

int numMistakes = 1337;

You can declare several variables of the same type all at once by separating them with commas. You can even initialize them if you like. The following code declares three float variables named x, y, and z and gives them initial values of 1, 2, and –40, respectively:

float x = 1, y = 2, z = -40;

NOTE

The program must assign a value to a variable before it tries to read its value. For example, C# flags the following code as an error because the second line tries to use x on the right-hand side of the equals sign to calculate y before x has been assigned a value:

int x, y;

y = x + 1;

Literal Values

A literal value is a piece of data stuck right in the code. For example, in the following statement, numMistakes is a variable and 1337 is a literal integer value:

int numMistakes = 1337;

Usually C# is pretty smart about using the correct data types for literal values. For example, in the preceding statement C# knows that numMistakes is an integer and 1337 is an integer, so it can safely put the integer value in the integer variable.

Sometimes, however, C# gets confused and assumes a literal value has a data type other than the one you intend. For example, the following code declares a float variable named napHours and tries to assign it the value 6.5. Unfortunately, C# thinks 6.5 is a double and a double won’t fit inside a float variable, so it flags this as an error:

float napHours = 6.5;

In cases such as this one, you can help C# understand what data type a literal has by adding a suffix character. For example, the F character in the following code tells C# that it should treat 6.5 as a float, not a double:

float napHours = 6.5F;

Table 11.2 lists C#’s data type suffix characters. You can use the suffixes in either lower- or uppercase.

Table 11.2

Data Type

Suffix

Uint

U

Long

L

Ulong

UL orLU

Float

F

double

D

decimal

M

The int data type doesn’t have a literal suffix character. C# assumes a literal that looks like an integer is an int, unless it’s too big, in which case it assumes the value is a long. For example, it assumes that 2000000000 is an int because that value will fit in an int. It assumes that 3000000000 is a long because it’s too big to fit in an int.

The byte, sbyte, short, and ushort data types also have no literal suffix characters. Fortunately, you can assign an integer value to these types and C# will use the value correctly, as long as it fits.

You can use double quotes to surround strings and single quotes to surround chars as in the following code:

string firstName = "William";

string lastName = "Gates";

char middleInitial = 'H';

Sometimes you might like to include a special character such as a carriage return or tab character in a string literal. Unfortunately, you can’t simply type a carriage return into a string because it would start a new line of code, and that would confuse Visual Studio.

To work around this dilemma, C# provides escape sequences that represent special characters. An escape sequence is a sequence of characters that represent a special character such as a carriage return or tab.

Table 11.3 lists C#’s escape sequences.

Table 11.3

Sequence

Meaning

\a

Bell

\b

Backspace

\f

Formfeed

\n

Newline

\r

Carriage return

\t

Horizontal tab

\v

Vertical tab

\'

Single quotation mark

\"

Double quotation mark

\\

Backslash

\?

Question mark

\OOO

ASCII character in octal (OOO represents the octal code)

\xhh

ASCII character in hexadecimal (hh represents the hexadecimal code)

\xhhhh

Unicode character in hexadecimal (hhhh represents the hexadecimal code)

For example, the following code makes a variable that refers to a string that contains quotes and a newline character:

string txt = "Unknown value \"ten.\"\nPlease enter a number.";

When you display this string in a MessageBox, the user sees text similar to the following:

Unknown value "ten."

Please enter a number.

NOTE

When you display text in a Label (or MessageBox), you can start a new line by using the newline escape (\n). When you display text in a TextBox, however, you must start a new line by using the carriage return and newline escapes together (\r\n). (The\r\n sequence also works for Labels and MessageBoxes.)

C# also provides a special verbatim string literal that makes using some special characters easier. This kind of value begins with @" and ends with a corresponding closing quote ("). Between the quotes, the literal doesn’t know anything about escape sequences and treats every character literally.

A verbatim string literal cannot contain a double quote because that would end the string. It can’t even use an escaped double quote because verbatim string literals don’t understand escape sequences.

Verbatim string literals are very useful if you need a string that contains a lot of backslashes such as a Windows directory path (C:\Tools\Binary\Source\C#\PrintInvoices) or that needs to describe escape sequences themselves ("Use \r\n to start a new line").

You can even type new lines and tab characters inside a string literal, although those may make your code harder to read.

Type Conversions

C# performs implicit data type conversions where it knows the conversion is safe. For example, the following code declares a long variable and sets it equal to the int value 6. Because an int can always fit in a long, C# knows this is safe and doesn’t complain:

long numBananas = 6;

The converse is not always true, however. A long value cannot always fit in an int variable. Because it cannot know for certain that any given long will fit in an int, C# won’t quietly sit by while your code assigns a long value to an int.

For example, the following code assigns a value to a long variable. It then tries to save that long value into an int variable. At this point, C# panics and flags the line as an error:

long numBananas = 6;

int numFruits = numBananas;

In cases such as this, you can use three methods to coerce C# into converting data from one type to another: casting, converting, and parsing.

Casting

To cast a value from one data type to another, you put the target data type inside parentheses in front of the value. For example, the following code explicitly converts the variable numBananas into an int:

long numBananas = 6;

int numFruits = (int)numBananas;

Casting works only between compatible data types. For example, because double and int are both numbers, you can try to cast between them. (When you cast from a double to an int, the cast simply discards any fractional part of the value with no rounding.) In contrast, the string and bool data types are not compatible with the numeric data types or each other, so you cannot cast between them. (What would the statement (int)"platypus" even mean?)

Normally a cast doesn’t check whether it can succeed. If you try to convert a long into an int and the long won’t fit, C# sweeps its mistake under the rug like a politician in an election year, and the program keeps running. The value that gets shoved into the int may be gibberish, but the program doesn’t crash.

If the int now contains garbage, any calculations you perform with it will also be garbage so, in many cases, it’s better to let your program throw a tantrum and crash. (Lesson 21 explains how to catch errors such as this so you can do something more constructive than merely crash.)

To make C# flag casting errors, surround the cast in parentheses and add the word checked in front as in the following code:

long worldPopulation = 7309000000;

int peopleInWorld = checked((int)worldPopulation);

Now when the code executes at run time, the program will fail on the second statement because the value is too big to fit in an int.

NOTE

If you have several statements that you want to check, you can make a checked block. In the following code, both of the statements between the braces are checked:

long worldPopulation = 7309000000;

long asiaPopulation = 4428000000;

checked

{

int peopleInWorld = (int)worldPopulation;

int peopleInAsia = (int)asiaPopulation;

}

The checked keyword also checks integer calculations for overflow. For example, if you multiply two huge int variables together, the result won’t fit in an int. Normally the program keeps running without complaint even though the result overflowed, so it isn’t what you expect.

If you are working with values that might overflow and you want to be sure the results make sense, protect the calculations with checked.

Converting

Casting only works between compatible types. The Convert utility class (which is provided by the .NET Framework) gives you methods that you can use to try to convert values even if the data types are incompatible. These are shared methods provided by the Convertclass itself, so you don’t need to create an instance of the class to use them.

For example, the bool and int data types are not compatible, so C# doesn’t let you cast from one to the other. Occasionally, however, you might want to convert an int into a bool or vice versa. In that case you can use the Convert class’s ToBoolean and ToInt32 methods. (You use ToInt32 because ints are 32-bit integers.)

The following code declares two int variables and assigns them values. It uses Convert to change them into bools and then changes one of them back into an int:

int trueInt = -1;

int falseInt = 0;

bool trueBool = Convert.ToBoolean(trueInt);

bool falseBool = Convert.ToBoolean(falseInt);

int anotherTrueInt = Convert.ToInt32(trueBool);

NOTE

When you treat integer values as booleans, the value 0 is false and all other values are true. If you convert the bool literal value true into an integer value, you get –1.

In a particularly common scenario, a program must convert text entered by the user into some other data type such as an int or decimal. The following uses the Convert.ToInt32 method to convert whatever the user entered in the ageTextBox into an int:

int age = Convert.ToInt32(ageTextBox.Text);

This conversion works only if the user enters a value that can be reasonably converted into an int. If the user enters 13,914 or –1, the conversion works. If the user enters “seven,” the conversion fails.

Converting text into another data type is more properly an example of parsing than of data type conversion, however. So although the Convert methods work, your code will be easier to read and understand if you use the parsing methods described in the next section.

Parsing

Trying to find structure and meaning in text is called parsing. All of the simple data types (int, double, decimal) provide a method that converts text into that data type. For example, the int data type’s Parse method takes a string as a parameter and returns an int. At least it does if the string contains an integer value.

The following code declares a decimal variable named salary, uses the decimal class’s Parse method to convert the value in the salaryTextBox into a decimal, and saves the result in the variable:

decimal salary = decimal.Parse(salaryTextBox.Text);

As is the case with the Convert methods, this works only if the text can reasonably be converted into a decimal. If the user types “12,345.67,” the parsing works. If the user types “ten” or “1.2.3,” the parsing fails.

NOTE

Unfortunately, C#’s conversion and parsing methods get confused by some formats that you might expect them to understand. For example, they can’t handle currency characters, so they fail on strings like “$12.34” and “€54.32.”

You can tell the decimal class’s Parse method to allow currency values by passing it a second parameter, as shown in the following code:

decimal salary = decimal.Parse(salaryTextBox.Text,

System.Globalization.NumberStyles.Any);

Performing Calculations

You’ve already seen several pieces of code that assign a value to a variable. For example, the following code converts the text in the salaryTextBox into a decimal and saves it in the variable salary:

decimal salary = decimal.Parse(salaryTextBox.Text);

You can also save a value that is the result of a more complex calculation into a variable on the left side of an equals sign. Fortunately, the syntax for these kinds of calculations is usually easy to understand. The following code calculates the value 2736 + 7281 / 3 and saves the result in the variable result:

double result = 2736 + 7281 / 3;

The operands (the values used in the expression on the right) can be literal values, values stored in variables, or the results of methods. For example, the following code calculates the sales tax on a purchase’s subtotal. It multiplies the tax rate stored in the taxRatevariable by the decimal value stored in the subtotalTextBox and saves the result in the variable salesTax:

decimal salesTax = taxRate * decimal.Parse(subtotalTextBox.Text);

Note that a variable can appear on both sides of the equals sign. In that case, the value on the right is the variable’s current value and, after the calculation, the new result is saved back into the same variable.

For example, the following code takes x’s current value, doubles it, adds 10, and saves the result back in variable x. If x started with the value 3, then when this statement finishes x holds the value 16:

x = 2 * x + 10;

A variable may appear more than once on the right side of the equals sign but it can appear only once on the left.

The following sections provide some additional details about performing calculations.

Operands and Operators

One issue that confuses some people is the fact that C# uses the data types of an expression’s operands to determine the way the operators work. If an expression contains two ints, the operators use integer arithmetic. If an expression contains two floats, the operators use floating-point arithmetic.

Sometimes this can lead to confusing results. For example, the following code tries to save the value 1/7 in the float variable ratio. The values 1 and 7 are integers so this calculation uses integer division, which discards any remainder. Because 1 / 7 = 0 with a remainder of 1, ratio is assigned the value 0, which is probably not what you intended:

float ratio = 1 / 7;

To force C# to using floating-point division, you can convert the numbers into the float data type. The following code uses the F suffix character to indicate that 1 and 7 should have the float data type instead of int. Now the program performs floating-point division, so it assigns ratio the value 0.142857149 (approximately):

float ratio = 1F / 7F;

Instead of using data type suffixes, you can also use casting to make the program treat the values as floats as in the following code:

float ratio = (float)1 / (float)7;

Promotion

If an expression uses two different data types, C# promotes the one with the more restrictive type. For example, if you try to divide an int by a float, C# promotes the int to a float before it performs the division.

The following code divides a float by an int. Before performing the calculation, C# promotes the value 7 to a float. This is sometimes called implicit casting. The code then performs the division and saves the result 0.142857149 in the variable ratio:

float ratio = 1F / 7;

Operator Summary

C# has many operators for manipulating variables of different data types. The following sections describe the most commonly used operators grouped by operand type (arithmetic, string, logical, and so forth).

Remember that some operators behave differently depending on the data types of their operands.

Arithmetic Operators

The arithmetic operators perform calculations on numbers. Table 11.4 summarizes the arithmetic operators. The Example column shows sample results. For the final examples, assume that x is an int that initially has value 10.

Table 11.4

Operator

Meaning

Example

+

Addition

3 + 2 is 5

-

Negation

-3 is negative 3

-

Subtraction

3 - 2 is 1

*

Multiplication

3 * 2 is 6

/

Division (integer)

3 / 2 is 1

/

Division (floating point)

3F / 2F is 1.5

%

Modulus

3 % 2 is 1

++

Pre-increment

++x: x is incremented to 11 and then the statement uses the new value 11

++

Post-increment

x++: the statement uses the current value of x, 10, and then x is incremented to 11

- -

Pre-decrement

--x: x is decremented to 9 and then the statement uses the new value 9

- -

Post-decrement

x--: the statement uses the current value of x, 10, and then x is decremented to 9

Integer division discards any remainder and returns the integer quotient. The modulus operator, which applies only to integer data types, does the opposite: it discards the quotient and returns the remainder. For example, 17 % 5 returns 2 because 17 divided by 5 is 3with a remainder of 2.

The pre- and post-increment and decrement operators return a value either before or after it is incremented or decremented. For example, the following code sets x equal to 10 + y = 20 and then adds 1 toy. When the code finishes, x = 20 and y = 11:

int x, y = 10;

x = 10 + y++;

In contrast, the following code increments y first and then uses the new value to calculate x. When this code finishes, x = 21 and y = 11:

int x, y = 10;

x = 10 + ++y;

The decrement operators work similarly except they subtract 1 instead of add 1.

The increment and decrement operators can be very confusing, particularly when they’re in the middle of a complex expression. If you have trouble with them, simply don’t use them. For example, the following code gives you the same result as the previous code but without the pre-increment operator:

int x, y = 10;

y = y + 1;

x = 10 + y;

Logical Operators

The logical operators perform calculations on boolean (true or false) values. They let you combine logical statements to form new ones.

Lesson 18 explains how to use these values to perform tests that let a program take action only under certain circumstances. For example, a program might pay an employee overtime if the employee is hourly and worked more than 40 hours in the last week.

Table 11.5 summarizes the boolean operators.

Table 11.5

Operator

Meaning

&

AND

|

OR

^

XOR

!

NOT

&&

Conditional AND

||

Conditional OR

The & operator returns true if and only if both of its operands are true. For example, you must buy lunch if it’s lunchtime and you forgot to bring a lunch today:

mustBuyLunch = isLunchTime & forgotToBringLunch;

The | operator returns true if either of its operands is true. For example, you can afford lunch if either you brought enough money or you have a credit card (or both):

canAffordLunch = haveEnoughMoney | haveCreditCard;

The ^ operator (the exclusive OR operator) is the most confusing. It returns true if one of its operands is true and the other is false. For example, you and Ann will get a single lunch check and pay each other back later if either Ann forgot her money and you brought yours or Ann remembered her money and you forgot yours. If neither of you forgot your money, you can get separate checks. If you both forgot your money, you’re both going hungry today:

singleCheck = annForgotMoney ^ youForgotMoney;

The ! operator returns true if its single operand is false. For example, if the cafeteria is not closed, you can have lunch there:

canHaveLunch = !cafeteriaIsClosed;

The conditional operators, which are also called short-circuit operators, work just like the regular ones except they don’t evaluate their second operand unless they must. For example, consider the following AND statement:

mustBuyLunch = isLunchTime && forgotToBringLunch;

Suppose it’s only 9:00 a.m. so isLunchTime is false. When the program sees this expression, it evaluates isLunchTime and then encounters the && operator. Because isLunchTime is false, the program already knows that mustBuyLunch must also be false no matter what value follows the && (in this case forgotToBringLunch). In that case, the program doesn’t bother to evaluate forgotToBringLunch and that saves a tiny amount of time.

Similarly, consider the following OR statement:

canAffordLunch = haveEnoughMoney || haveCreditCard;

If you have enough money, haveEnoughMoney is true, so the program doesn’t need to evaluate haveCreditCard to know that the result canAffordLunch is also true.

Because the conditional && and || operators are slightly faster, most developers use them whenever they can instead of using & and |.

NOTE

There is one case where the conditional operators may cause problems. If the second operand is not a simple value but the result returned from some sort of method call, then if you use a conditional operator, you cannot always know whether the method was called. This might matter if the method has side effects: consequences that last after the method has finished, like opening a database or creating a file. In that case, you cannot know later whether the database is open or the file is created.

This is seldom a problem and you can avoid it completely by avoiding side effects.

String Operators

The only string operator C# provides is +. This operator concatenates (joins) two strings together. For example, suppose the variable username contains the user’s name. Then the following code concatenates the text “Hello ” (note the trailing space) with the user’s name and displays the result in a message box:

MessageBox.Show("Hello " + username);

Lesson 14 explains methods that you can use to manipulate strings: find substrings, replace text, check length, and so forth.

NOTE

One very non-obvious fact about string operations is that a string calculation does not really save the results in the same memory used by the variable on the left of an assignment statement. Instead it creates a new string holding the result of the calculation and makes the variable refer to that.

For example, consider the following code:

string greeting = usernameTextBox.Text;

greeting = "Hello " + greeting;

This code looks like it saves a user’s name in the variable greeting and then tacks “Hello” onto the front. Actually, the second statement creates a whole new string that holds “Hello” plus the user’s name and then makes greeting refer to the new string.

For many practical applications, the difference is small, and you can ignore it. However, if you’re performing a huge number of concatenations (perhaps in one of the loops described in Lesson 19), your program might have performance issues. TheStringBuilder class can help address this issue, but it’s a bit more advanced so I’m not going to cover it here. See msdn.microsoft.com/library/2839d5h5.aspx for more information.

Comparison Operators

The comparison operators compare two values and return true or false depending on the values’ relationship. For example, x < y returns true if x is less than y.

Table 11.6 summarizes the comparison operators.

Table 11.6

Operator

Meaning

Example

==

Equals

2 == 3 is false

!=

Not equals

2 != 3 is true

<

Less than

2 < 3 is true

<=

Less than or equal to

2 <= 3 is true

>

Greater than

2 > 3 is false

>=

Greater than or equal to

2 >= 3 is false

Bitwise Operators

The bitwise operators enable you to manipulate the individual bits in integer values. For example, the bitwise | operator combines the bits in two values so the result has a bit equal to 1 wherever either of the two operands has a bit equal to one.

For example, suppose x and y are the byte values with bits 10000000 and 00000001. Then x | y has bits 10000001.

This may be easier to understand if you write y below x as in the following:

x: 10000000

y: 00000001

--------

Result: 10000001

Now it’s easy to see that the result has a 1 where either x or y had a 1.

The bitwise operators are fairly advanced so I’m not going to do much with them, but Table 11.7 summarizes them. The shift operators are not “bitwise” because they don’t compare two operands one bit at a time, but they are bit-manipulation operators so they’re included here.

Table 11.7

Operator

Meaning

Example

&

Bitwise AND

11110000
& 00111100
= 00110000

|

Bitwise OR

11110000
| 00111100
= 11111100

^

Bitwise XOR

11110000
^ 00111100
= 11001100

~

Bitwise complement

~11110000
= 00001111

<<

Left shift

11100111 << 2
= 10011100

>>

Right shift (for signed types)

11100111 >> 2
= 11111001

>>

Right shift (for unsigned types)

11100111 >> 2
= 00111001

If the operand has a signed type (such as sbyte, int, or long), then >> makes new bits on the left be copies of the value’s sign bit (its leftmost bit). If the operand has an unsigned type (byte, uint, ulong), then >> makes the new bits 0.

All of these except ~ also have corresponding compound assignments operators, for example, &= and <<=. Compound assignment operators are described in the next section.

Assignment Operators

The assignment operators set a variable (or property or whatever) equal to something else. The simplest of these is the = operator, which you have seen several times before. This operator simply assigns whatever value is on the right to the variable on the left.

The other assignment operators, which are known as compound assignment operators, combine the variable’s current value with whatever is on the right in some way. For example, the following code adds 3 to whatever value x currently holds:

x += 3;

This has the same effect as the following statement that doesn’t use the += operator:

x = x + 3;

Table 11.8 summarizes the assignment operators. For the examples, assume i is an int, x is a float, and a and b are bools.

Table 11.8

Operator

Meaning

Example

Means

=

Assign

x = 10;

x = 10;

+=

Add and assign

x += 10;

x = x + 10;

-=

Subtract and assign

x -= 10;

x = x - 10;

*=

Multiply and assign

x *= 10;

x = x * 10;

/=

Divide and assign

x /= 10;

x = x / 10;

%=

Modulus and assign

x %= 10;

x = x % 10;

&=

Logical AND and assign

a &= b;

a = a & b;

|=

Logical OR and assign

a |= b;

a = a | b;

^=

Logical XOR and assign

a ^= b;

a = a ^ b;

<<=

Left shift and assign

i <<= 3;

i = i << 3;

>>=

Right shift and assign

i >>= 5;

i = i >> 5;

Precedence

Sometimes the order in which you evaluate the operators in an expression changes the result. For example, consider the expression 2 + 3 * 5. If you evaluate the + first, you get 5 * 5, which is 25, but if you evaluate the * first, you get 2 + 15, which is 17.

To prevent any ambiguity, C# defines operator precedence to determine which comes first.

Table 11.9 lists the major operators in order of decreasing precedence. In other words, the operators listed near the beginning of the table are applied before those listed later. Operators listed at the same level have the same precedence and are applied in left-to-right order.

Table 11.9

Category

Operators

Primary

x++,x- -

Unary

+, -,!, ++x, - -x

Multiplicative

*,/,%

Additive

+,-

Relational

<,<=,>,>=

Equality

==,!=

Logical AND

&

Logical XOR

^

Logical OR

|

Conditional AND

&&

Conditional OR

||

The compound assignment operators (+=, *=, ^=, and so forth) always have lowest precedence. The program evaluates the expression on the right, combines it with the original value of the variable on the left, and then saves the result in that variable.

By carefully using the precedence rules, you can always figure out how a program will evaluate an expression, but sometimes the expression can be confusing enough to make figuring out the result difficult. Trying to figure out precedence in confusing expressions can be a great party game (a programmer’s version of “Pictionary”), but it can make understanding and debugging programs hard.

Fortunately you can always use parentheses to change the order of evaluation or to make the default order obvious. For example, consider the following three statements:

x = 2 + 3 * 5;

y = 2 + (3 * 5);

z = (2 + 3) * 5;

The first statement uses no parentheses so you need to use the precedence table to figure out which operator is applied first. The table shows that * has higher precedence than +, so * is applied first and the result is 2 + 15, which is 17.

The second statement uses parentheses to emphasize the fact that the * operator is evaluated first. The result is unchanged, but the code is easier to read.

The third statement uses parentheses to change the order of evaluation. In this case the + operator is evaluated first, so the result is 5 * 5, which is 25.

NOTE

Parentheses are a useful tool for making your code easier to understand and debug. Unless an expression is so simple that it’s obvious how it is evaluated, add parentheses to make the result clear.

Constants

A constant is a lot like a variable except you must assign it a value when you declare it and you cannot change the value later.

Syntactically a constant’s declaration is similar to a variable except it uses the keyword const.

For example, the bold line in the following code declares a decimal constant named taxRate and assigns it the value 0.09M. The code then uses the constant in a calculation:

const decimal taxRate = 0.09M;

decimal subtotal = decimal.Parse(subtotalTextBox.Text);

decimal salesTax = taxRate * subTotal;

decimal grandTotal = subTotal + salesTax;

Constants work just like literal values, so you could replace the constant taxRate with the literal value 0.09M in the preceding calculation. Using a constant makes the code easier to read, however. When you see the value 0.09M, you need to remember or guess that this is a tax rate.

Not only can it be hard to remember what this kind of “magic number” means, but it can also make changing the value difficult if it appears in many places throughout the program. Suppose the code uses the value 0.09M in several places. If the sales tax rate went up to 0.10M, you would have to hunt down all of the occurrences of that number and change them. If you miss some of them, you could get very confusing results. Things could be even more confusing if the program also used 0.09M in some places to represent other values. If you changed them to 0.10M, you would break the code that uses those values.

Note that constants can contain calculated values as long as C# can perform the calculation before the program actually runs. For example, the following code declares a constant that defines the number of centimeters per inch. It then uses that value to define the number of centimeters per foot:

const double cmPerInch = 2.54;

const double cmPerFoot = cmPerInch * 12;

Try It

In this Try It you make some simple calculations. You take values entered by the user, convert them into numbers, do some multiplication and addition, and display the results.

Lesson Requirements

In this lesson, you:

· Create the form shown in Figure 11.1.Sales Tax Calculator window with 4 columns (top) for Items, Quantity, Price Each, and Item Total and (bottom) textboxes for labels Subtotal, Tax Rate, Sales Tax, Shipping, and Grand Total, and Calculate button.

Figure 11.1

· When the user clicks the Calculate Button, make the program:

· Multiply each item’s Quantity value by its Price Each value and display the result in the corresponding Item Total TextBox.

· Add up the Item Total values and display the result in the Subtotal TextBox.

· Multiply the Subtotal value by the entered Tax Rate and display the result in the Sales Tax TextBox.

· Add the Subtotal, Sales Tax, and Shipping values, and display the result in the Grand Total TextBox.

NOTE

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

Hints

· It is often helpful to perform this kind of calculation in three separate phases:

1. Gather input values from the user and store them in variables.

2. Perform calculations.

3. Display results.

· Use the decimal data type for the variables that represent currency values.

· Lesson 14 has more to say about manipulating and formatting strings, but for this Try It it’s helpful to know that all data types provide a ToString method that converts a value into a string. An optional parameter string indicates the format to use. For this Try It, use the format "C" (including the quotes) to indicate a currency format, as in:

grandTotalTextBox.Text = grandTotal.ToString("C");

· If the program tries to perform the calculations and some of the values it needs are missing (for example, if one of the Price Each TextBoxes is empty), the program will crash. Don’t worry about it for now.

Step-by-Step

· Create the form shown in Figure 11.1.

1. Create the controls needed for the program shown in Figure 11.1.

a. The Quantity values are NumericUpDown controls.

b. All of the other box-like controls are TextBoxes.

c. The output controls (for the Item Total values, Subtotal, Sales Tax, and Grand Total) are TextBoxes with ReadOnly set to True.

d. Set the form’s AcceptButton property to the Calculate Button.

2. Give names to the controls that the program needs to manipulate. That includes the NumericUpDown controls, all of the TextBoxes, and the Button.

· When the user clicks the Calculate Button, make the program:

· Multiply each item’s Quantity value by its Price Each value and display the result in the corresponding Item Total TextBox.

· Add up the Item Total values and display the result in the Subtotal TextBox.

· Multiply the Subtotal value by the entered Tax Rate and display the result in the Sales Tax TextBox.

· Add the Subtotal, Sales Tax, and Shipping values, and display the result in the Grand Total TextBox.

This is easy to do in three steps:

5. Gather input values from the user and store them in variables. Because they are already numeric, the code doesn’t need to parse the values that come from the NumericUpDown control’s Value properties. The program does need to parse the values in TextBoxes to convert them into decimal values:

6. // Get input values.

7. decimal quantity1 = qty1NumericUpDown.Value;

8. decimal quantity2 = qty2NumericUpDown.Value;

9. decimal quantity3 = qty3NumericUpDown.Value;

10. decimal quantity4 = qty4NumericUpDown.Value;

11. decimal priceEach1 = decimal.Parse(priceEach1TextBox.Text);

12. decimal priceEach2 = decimal.Parse(priceEach2TextBox.Text);

13. decimal priceEach3 = decimal.Parse(priceEach3TextBox.Text);

14. decimal priceEach4 = decimal.Parse(priceEach4TextBox.Text);

15. decimal taxRate = decimal.Parse(taxRateTextBox.Text);

decimal shipping = decimal.Parse(shippingTextBox.Text);

16.Perform calculations. In this Try It, the calculations are pretty simple. To keep the code simple, the program uses a separate variable for each result instead of tries to add them all up at once:

17. // Calculate results.

18. decimal total1 = quantity1 * priceEach1;

19. decimal total2 = quantity2 * priceEach2;

20. decimal total3 = quantity3 * priceEach3;

21. decimal total4 = quantity4 * priceEach4;

22. decimal subtotal = total1 + total2 + total3 + total4;

23. decimal salesTax = subtotal * taxRate;

decimal grandTotal = subtotal + salesTax + shipping;

24.Display results. The program uses ToString("C") to display values in a currency format:

25. // Display results.

26. total1TextBox.Text = total1.ToString("C");

27. total2TextBox.Text = total2.ToString("C");

28. total3TextBox.Text = total3.ToString("C");

29. total4TextBox.Text = total4.ToString("C");

30. subtotalTextBox.Text = subtotal.ToString("C");

31. salesTaxTextBox.Text = salesTax.ToString("C");

grandTotalTextBox.Text = grandTotal.ToString("C");

Exercises

1. [WPF] Repeat the Try It with a WPF program.

2. When the user changes a value used in a calculation, it can be confusing if the program displays old calculated values. Copy the program you built for the Try It and make these modifications:

· Disable the Calculate Button.

· When the user modifies any value used in the calculations, blank the calculated TextBoxes and enable the Calculate Button.

· After it displays the calculated values, make the Button’s code disable the Button again.

3. [WPF] Repeat Exercise 2 with the program you built for Exercise 1. Hint: When the program first starts, the TextBoxes will fire their TextChanged events, but not all of the TextBoxes will have been built yet, so the program can’t clear their text. To avoid crashing, make the event handlers use the following statement to see if the window has finished loading before it starts clearing TextBoxes:

if (!IsLoaded) return;

4. Copy the program you built for Exercise 2. As it stands, the program crashes if any of the input values it needs are missing. Modify the program to prevent that by enabling the Calculate Button only if all of the needed values are present. Hints:

· In the event handlers, disable the Calculate Button. Then use code similar to the following for each of the required values before you re-enable the Button:

if (priceEach1TextBox.Text.Length == 0) return;

· The program will still crash if the user enters a non-numeric value such as “ten.” Don’t worry about that for now. You’ll learn how to fix that in Lesson 21.

5. [WPF] Repeat Exercise 4 with the program you built for Exercise 3.

6. The program you built for Exercise 4 doesn’t understand currency values. For example, if you enter $6.00 in a Price Each TextBox and click Calculate, the program crashes. Fix that. Hints:

· Use code similar to the following to allow currency values, thousands separators, parentheses, leading and trailing signs, and other numeric formats:

· decimal priceEach1 = decimal.Parse(priceEach1TextBox.Text,

System.Globalization.NumberStyles.Any);

· Don’t make that change for the quantity values or the tax rate because they’re not currency values.

7. [WPF] Repeat Exercise 6 with the program you built for Exercise 5.

8. Make a program similar to the one shown in Figure 11.2. When the user checks or unchecks either of the A or B CheckBoxes, the program should check or uncheck the result CheckBoxes appropriately. For example, if A and B are both checked, the A && B CheckBoxshould also be checked.Logical Operator window displaying (top) Inputs panel with checkboxes for A (checked) and B and (bottom) Results panel displaying six checkboxes with three boxes checked.

Figure 11.2

Hints:

· Set a result CheckBox’s Checked property equal to a boolean expression. For example:

aAndBCheckBox.Checked = aCheckBox.Checked && bCheckBox.Checked;

· To make a CheckBox’s caption display an ampersand, place two in its Text property. To display two ampersands, use four in the Text property as in “A &&&& B.”

· The last CheckBox is checked at the same time as one of the others. Which one? Does that make sense?

9. A program can get information about the operating system in many ways. Three useful values include:

· Environment.UserName—The current user’s name.

· DateTime.Now.ToShortTimeString()—The current time in short format.

· DateTime.Now.ToShortDateString()—The current date in short format.

Make a program that greets the user when it starts by displaying a message box similar to the one shown in Figure 11.3. (Hint: You’ll need to concatenate several strings.)

10.Message box displaying message “Hello Rod. It is now 10:43 AM on 4/1/2031.” and OK button at the bottom left of the message.

11. Figure 11.3

12.Copy the program you wrote for Exercise 9 and make it display its greeting in a Label instead of a message box.

13.Make a program to determine whether 12345 * 54321 > 22222 * 33333. In three Labels, display the result of 12345 * 54321, the result of 22222 * 33333, and the boolean value 12345 * 54321 > 22222 * 33333. The final value should be true or false. (Hint: UseToString to convert the boolean result into a string.)

14.Make a program that converts degrees Celsius to degrees Fahrenheit. It should have two TextBoxes with associated Buttons. When the user enters a value in the Celsius TextBox and clicks its Button, the program converts the value into degrees Fahrenheit and displays the result in the other TextBox. Make the other Button convert from Fahrenheit to Celsius. Hints:

· °F = °C * 9 / 5 + 32 and °C = (°F – 32) * 5 / 9.

· What’s special about the temperature –40° Celsius?

15.Make a currency converter that converts between U.S. dollars, British pounds, Euros, Japanese yen, Indian rupees, and Swiss francs. Make constants for the following conversion factors (or go online and look up the current exchange rates):

16. // Exchange rates in USD.

17. const decimal eurPerUsd = 0.68M;

18. const decimal gbpPerUsd = 0.63M;

19. const decimal jpyPerUsd = 89.16M;

20. const decimal inrPerUsd = 47.24M;

const decimal chfPerUsd = 1.03M;

To make the constants usable by every event handler in the program, place these declarations outside of any event handler. (Right after the end of the Form1 method would work.)

Make a TextBox and Button for each currency. When the user clicks the Button, the program should:

· Get the value in the corresponding TextBox.

· Convert that value into U.S. dollars.

· Use the converted value in U.S. dollars to calculate the other currency values.

· Display the results.

21.Make a program similar to the one you made for Exercise 13 but make this one convert between inches, feet, yards, miles, centimeters, meters, and kilometers.

22.[Games] Make a program that contains a PictureBox (holding a picture of something that flies) and a Timer (with Interval = 50).

Inside the code but outside of any event handler, declare two double variables named Theta and Dtheta initialized to 0 and Math.PI / 30, respectively. (System.Math contains several useful mathematical values and methods including Sin and Cos, which you’ll use in a moment.)

When the user clicks the PictureBox, enable or disable the Timer.

In the Timer's Tick event handler, move the PictureBox to the point: (100 + 100 * Math.Cos(Theta), 100 + 75 * Math.Sin(Theta)) Then add Dtheta to Theta. (Convert data types if necessary.)

23.[Games] Copy the program you built for Exercise 15 and add an HScrollBar with Minimum = 1, Maximum = 10, and LargeChange = 1. In its Scroll event handler, display the new value in a read-only TextBox and set the Timer’s Interval property to:110 - 10 * speedScrollBar.Value.

24.[Games, Advanced] One way to handle projectile motion is to use variables Vx and Vy to represent an object’s velocities in the X and Y directions, respectively. At every tick of a Timer, you add Vx and Vy to the object’s current X and Y coordinates, respectively. For projectile motion, you then add a downward acceleration due to gravity to Vy.

For this exercise, build a program similar to the one shown in Figure 11.4 to simulate projectile motion. To keep the program simple for the user, the angle is in degrees, the speed is in feet per second, and the scale is 1 pixel = 1 foot.

Screenshot of Projectile Motion window displaying on top entry boxes with 60 for Angle and 1200 for Speed and Fire and Stop buttons. A dark shaded circle depicting cannonball is located below.

Figure 11.4

Hints:

· Build the form as shown in Figure 11.4. The cannonball is an image displayed in a PictureBox. Also add a Timer named moveTimer and set its Interval property to 50.

· Outside of any event handler, create six float variables named TicksPerSecond, X, Y, Vx, Vy, and Ay.

· When the user clicks the Fire Button:

§ Use the form’s ClientSize and the PictureBox’s Size to move the PictureBox to the form’s lower-left corner.

§ Parse the angle and speed entered by the user.

§ Convert the angle from degrees to radians by using the formula:radians =degrees * Math.PI / 180.

§ Calculate the number of Timer ticks per second by using the formula: TicksPerSecond = 1000 / moveTimer.Interval.

§ Use the following equations (converting data types as necessary) to calculate the ball’s initial velocities Vx and Vy in feet per tick:

§ Vx = speed * Math.Cos(radians) / TicksPerSecond

Vy = speed * Math.Sin(radians) / TicksPerSecond

§ Use the following equation to calculate the ball’s acceleration due to gravity in feet per tick per tick:

Ay = 32 / TicksPerSecond / TicksPerSecond

§ Enable the Timer.

§ Disable the Fire Button.

§ Enable the Stop Button.

· When the user clicks the Stop Button:

§ Disable the Timer.

§ Enable the Fire Button.

§ Disable the Stop Button.

· When the Timer’s Tick event fires:

§ Move the cannonball by adding Vx to the PictureBox’s Left property and subtracting Vy from the PictureBox’s Top property. (You subtract Vy because Y coordinates on the form decrease upward.)

§ Add the downward acceleration due to gravity to Vy by subtracting Ay

· If all goes well, then for a 60° angle and a speed of 120 feet per second, the cannonball should take around 8.5 seconds to drop off the bottom of the form. (It may seem like a long time, but the ball travels more than 400 feet horizontally during that time.)

NOTE

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