This lesson introduces C# operators, types, and variables. Its goal is to meet the following objectives:

- Understand what a variable is.
- Familiarization with C# built-in types.
- Get an introduction to C# operators.
- Learn how to use Arrays.

**Variables and Types**

"Variables" are simply storage locations for data. You can place data into them and retrieve their contents as part of a C# expression. The interpretation of the data in a variable is controlled through "Types".

C# is a "Strongly Typed" language. Thus all operations on variables are performed with consideration of what the variable's "Type" is. There are rules that define what operations are legal in order to maintain the integrity of the data you put in a variable.

The C# simple types consist of the Boolean type and three numeric types - Integrals, Floating Point, Decimal, and String. The term "Integrals", which is defined in the C# Programming Language Specification, refers to the classification of types that include sbyte, byte, short, ushort, int, uint, long, ulong, and char. More details are available in the Integral Types section later in this lesson. The term "Floating Point" refers to the float and double types, which are discussed, along with the decimal type, in more detail in the Floating Point and Decimal Types section later in this lesson. The string type represents a string of characters and is discussed in The String Type section, later in this lesson. The next section introduces the boolean type.

**The Boolean Type**

Boolean types are declared using the keyword,

*bool*. They have two values:*true*or*false*. In other languages, such as C and C++, boolean conditions can be satisfied where 0 means false and anything else means true. However, in C# the only values that satisfy a boolean condition is*true*and*false*, which are official keywords. Listing 2-1 shows one of many ways that boolean types can be used in a program.**Listing 2-1. Displaying Boolean Values: Boolean.cs**

using System;

class Booleans

{

public static void Main()

{

bool content = true;

bool noContent = false;

Console.WriteLine("It is {0} that C# Station provides C# programming language content.", content);

Console.WriteLine("The statement above is not {0}.", noContent);

}

}

class Booleans

{

public static void Main()

{

bool content = true;

bool noContent = false;

Console.WriteLine("It is {0} that C# Station provides C# programming language content.", content);

Console.WriteLine("The statement above is not {0}.", noContent);

}

}

In Listing 2-1, the boolean values are written to the console as a part of a sentence. The only legal values for the

*bool*type are either*true*or*false*, as shown by the assignment of*true*to*content*and*false*to*noContent*. When run, this program produces the following output: It is True that C# Station provides C# programming language content.

The statement above is not False.

The statement above is not False.

**Integral Types**

In C#, an

*integral*is a category of types. For anyone confused because the word Integral sounds like a mathematical term, from the perspective of C# programming, these are actually defined as Integral types in the C# programming language specification. They are whole numbers, either signed or unsigned, and the char type. The char type is a Unicode character, as defined by the Unicode Standard. For more information, visit The Unicode Home Page. table 2-1 shows the integral types, their size, and range.**Table 2-1. The Size and Range of C# Integral Types**

Type | Size (in bits) | Range |

sbyte | 8 | -128 to 127 |

byte | 8 | 0 to 255 |

short | 16 | -32768 to 32767 |

ushort | 16 | 0 to 65535 |

int | 32 | -2147483648 to 2147483647 |

uint | 32 | 0 to 4294967295 |

long | 64 | -9223372036854775808 to 9223372036854775807 |

ulong | 64 | 0 to 18446744073709551615 |

char | 16 | 0 to 65535 |

Integral types are well suited for those operations involving whole number calculations. The

*char*type is the exception, representing a single Unicode character. As you can see from the table above, you have a wide range of options to choose from, depending on your requirements.**Floating Point and Decimal Types**

A C# floating point type is either a float or double. They are used any time you need to represent a real number, as defined by IEEE 754. For more information on IEEE 754, visit the IEEE Web Site. Decimal types should be used when representing financial or money values. table 2-2 shows the floating point and decimal types, their size, precision, and range.

**Table 2-2. The Floating Point and Decimal Types with Size, precision, and Range**

Type | Size (in bits) | precision | Range |

float | 32 | 7 digits | 1.5 x 10 ^{-45} to 3.4 x 10^{38} |

double | 64 | 15-16 digits | 5.0 x 10 ^{-324} to 1.7 x 10^{308} |

decimal | 128 | 28-29 decimal places | 1.0 x 10 ^{-28} to 7.9 x 10^{28} |

Floating point types are used when you need to perform operations requiring fractional representations. However, for financial calculations, the

*decimal*type is the best choice because you can avoid rounding errors.**The string Type**

A string is a sequence of text characters. You typically create a string with a string literal, enclosed in quotes: "This is an example of a string." You've seen strings being used in Lesson 1, where we used the

*Console.WriteLine*method to send output to the console.Some characters aren't printable, but you still need to use them in strings. Therefore, C# has a special syntax where characters can be escaped to represent non-printable characters. For example, it is common to use newlines in text, which is represented by the '\n' char. The backslash, '\', represents the escape. When preceded by the escape character, the 'n' is no longer interpreted as an alphabetical character, but now represents a newline.

You may be now wondering how you could represent a backslash character in your code. We have to escape that too by typing two backslashes, as in '\\'. table 2-3 shows a list of common escape sequences.

**Table 2-3. C# Character Escape Sequences**

Escape Sequence | Meaning |

\' | Single Quote |

\" | Double Quote |

\\ | Backslash |

\0 | Null, not the same as the C# null value |

\a | Bell |

\b | Backspace |

\f | form Feed |

\n | Newline |

\r | Carriage Return |

\t | Horizontal Tab |

\v | Vertical Tab |

Another useful feature of C# strings is the verbatim literal, which is a string with a @ symbol prefix, as in

*@"Some string"*. Verbatim literals make escape sequences translate as normal characters to enhance readability. To appreciate the value of verbatim literals, consider a path statement such as*"c:\\topdir\\subdir\\subdir\\myapp.exe"*. As you can see, the backslashes are escaped, causing the string to be less readable. You can improve the string with a verbatim literal, like this:*@"c:\topdir\subdir\subdir\myapp.exe"*.That is fine, but now you have the problem where quoting text is not as easy. In that case, you would specify double double quotes. For example, the string

*"copy \"c:\\source file name with spaces.txt\" c:\\newfilename.txt"*would be written as the verbatim literal*@"copy ""c:\source file name with spaces.txt"" c:\newfilename.txt"*.**C# Operators**

Results are computed by building expressions. These expressions are built by combining variables and operators together into statements. The following table describes the allowable operators, their precedence, and associativity.

**Table 2-4. Operators with their precedence and Associativity**

Category (by precedence) | Operator(s) | Associativity |

Primary | x.y f(x) a[x] x++ x-- new typeof default checked unchecked delegate | left |

Unary | + - ! ~ ++x --x (T)x | left |

Multiplicative | * / % | left |

Additive | + - | left |

Shift | << >> | left |

Relational | < > <= >= is as | left |

Equality | == != | right |

Logical AND | & | left |

Logical XOR | ^ | left |

Logical OR | | | left |

Conditional AND | && | left |

Conditional OR | || | left |

Null Coalescing | ?? | left |

Ternary | ?: | right |

Assignment | = *= /= %= += -= <<= >>= &= ^= |= => | right |

Left associativity means that operations are evaluated from left to right. Right associativity mean all operations occur from right to left, such as assignment operators where everything to the right is evaluated before the result is placed into the variable on the left.

Most operators are either unary or binary. Unary operators form expressions on a single variable, but binary operators form expressions with two variables. Listing 2-2 demonstrates how unary operators are used.

**Listing 2-2. Unary Operators: Unary.cs**

using System;

class Unary

{

public static void Main()

{

int unary = 0;

int preIncrement;

int preDecrement;

int postIncrement;

int postDecrement;

int positive;

int negative;

sbyte bitNot;

bool logNot;

preIncrement = ++unary;

Console.WriteLine("pre-Increment: {0}", preIncrement);

preDecrement = --unary;

Console.WriteLine("pre-Decrement: {0}", preDecrement);

postDecrement = unary--;

Console.WriteLine("Post-Decrement: {0}", postDecrement);

postIncrement = unary++;

Console.WriteLine("Post-Increment: {0}", postIncrement);

Console.WriteLine("Final Value of Unary: {0}", unary);

positive = -postIncrement;

Console.WriteLine("Positive: {0}", positive);

negative = +postIncrement;

Console.WriteLine("Negative: {0}", negative);

bitNot = 0;

bitNot = (sbyte)(~bitNot);

Console.WriteLine("Bitwise Not: {0}", bitNot);

logNot = false;

logNot = !logNot;

Console.WriteLine("Logical Not: {0}", logNot);

}

}

class Unary

{

public static void Main()

{

int unary = 0;

int preIncrement;

int preDecrement;

int postIncrement;

int postDecrement;

int positive;

int negative;

sbyte bitNot;

bool logNot;

preIncrement = ++unary;

Console.WriteLine("pre-Increment: {0}", preIncrement);

preDecrement = --unary;

Console.WriteLine("pre-Decrement: {0}", preDecrement);

postDecrement = unary--;

Console.WriteLine("Post-Decrement: {0}", postDecrement);

postIncrement = unary++;

Console.WriteLine("Post-Increment: {0}", postIncrement);

Console.WriteLine("Final Value of Unary: {0}", unary);

positive = -postIncrement;

Console.WriteLine("Positive: {0}", positive);

negative = +postIncrement;

Console.WriteLine("Negative: {0}", negative);

bitNot = 0;

bitNot = (sbyte)(~bitNot);

Console.WriteLine("Bitwise Not: {0}", bitNot);

logNot = false;

logNot = !logNot;

Console.WriteLine("Logical Not: {0}", logNot);

}

}

When evaluating expressions, post-increment

*(x++)*and post-decrement*(x--)*operators return their current value and then apply the operators. However, when using pre-increment*(++x)*and pre-decrement*(--x)*operators, the operator is applied to the variable prior to returning the final value.In Listing 2-2, the

*unary*variable is initialized to zero. When the pre-increment*(++x)*operator is used,*unary*is incremented to 1 and the value 1 is assigned to the*preIncrement*variable. The pre-decrement*(--x)*operator turns*unary*back to a 0 and then assigns the value to the*preDecrement*variable.When the post-decrement

*(x--)*operator is used, the value of*unary*, 0, is placed into the*postDecrement*variable and then*unary*is decremented to -1. Next the post-increment*(x++)*operator moves the current value of*unary*, -1, to the*postIncrement*variable and then increments*unary*to 0.The variable

*bitNot*is initialized to 0 and the bitwise not*(~)*operator is applied. The bitwise not*(~)*operator flips the bits in the variable. In this case, the binary representation of 0, "00000000", was transformed into -1, "11111111".While the

*(~)*operator works by flipping bits, the logical negation operator*(!)*is a logical operator that works on*bool*values, changing*true*to*false*or*false*to*true*. In the case of the*logNot*variable in Listing 2-2, the value is initialized to*false*, and the next line applies the logical negation operator,*(!)*, which returns*true*and reassigns the new value,*true*, to*logNot*. Essentially, it is toggling the value of the*bool*variable,*logNot*.The setting of

*positive*is a little tricky. At the time that it is set, the*postIncrement*variable is equal to -1. Applying the minus*(-)*operator to a negative number results in a positive number, meaning that*postitive*will equal 1, instead of -1. The minus operator*(-)*, which is not the same as the pre-decrement operator (--), doesn't change the value of*postInc*- it just applies a sign negation. The plus operator*(+)*doesn't affect the value of a number, assigning*negative*with the same value as*postIncrement*, -1.Notice the expression

*(sbyte)(~bitNot)*. Any operation performed on types*sbyte*,*byte*,*short*, or*ushort*return*int*values. To assign the result into the*bitNot*variable we had to use a cast,*(Type)*, operator, where*Type*is the type you wish to convert to (in this case -*sbyte*). The cast operator is shown as the Unary operator,*(T)x,*in table 2-4. Cast operators must be performed explicity when you go from a larger type to a smaller type because of the potential for lost data. Generally speaking, assigning a smaller type to a larger type is no problem, since the larger type has room to hold the entire value. Also be aware of the dangers of casting between signed and unsigned types. You want to be sure to preserve the integrity of your data. Many basic programming texts contain good descriptions of bit representations of variables and the dangers of explicit casting.Here's the output from the Listing 2-2:

pre-Increment: 1

pre-Decrement 0

Post-Decrement: 0

Post-Increment: -1

Final Value of Unary: 0

Positive: 1

Negative: -1

Bitwise Not: -1

Logical Not: true

pre-Decrement 0

Post-Decrement: 0

Post-Increment: -1

Final Value of Unary: 0

Positive: 1

Negative: -1

Bitwise Not: -1

Logical Not: true

In addition to unary operators, C# has binary operators that form expressions of two variables. Listing 2-3 shows how to use the binary operators.

**Listing 2-3. Binary Operators: Binary.cs**

using System;

class Binary

{

public static void Main()

{

int x, y, result;

float floatresult;

x = 7;

y = 5;

result = x+y;

Console.WriteLine("x+y: {0}", result);

result = x-y;

Console.WriteLine("x-y: {0}", result);

result = x*y;

Console.WriteLine("x*y: {0}", result);

result = x/y;

Console.WriteLine("x/y: {0}", result);

floatresult = (float)x/(float)y;

Console.WriteLine("x/y: {0}", floatresult);

result = x%y;

Console.WriteLine("x%y: {0}", result);

result += x;

Console.WriteLine("result+=x: {0}", result);

}

}

class Binary

{

public static void Main()

{

int x, y, result;

float floatresult;

x = 7;

y = 5;

result = x+y;

Console.WriteLine("x+y: {0}", result);

result = x-y;

Console.WriteLine("x-y: {0}", result);

result = x*y;

Console.WriteLine("x*y: {0}", result);

result = x/y;

Console.WriteLine("x/y: {0}", result);

floatresult = (float)x/(float)y;

Console.WriteLine("x/y: {0}", floatresult);

result = x%y;

Console.WriteLine("x%y: {0}", result);

result += x;

Console.WriteLine("result+=x: {0}", result);

}

}

And here's the output:

x+y: 12

x-y: 2

x*y: 35

x/y: 1

x/y: 1.4

x%y: 2

result+=x: 9

x-y: 2

x*y: 35

x/y: 1

x/y: 1.4

x%y: 2

result+=x: 9

Listing 2-3 shows several examples of binary operators. As you might expect, the results of addition (+), subtraction (-), multiplication (*), and division (/) produce the expected mathematical results.

The

*floatresult*variable is a floating point type. We explicitly cast the integer variables*x*and*y*to calculate a floating point value.There is also an example of the remainder(%) operator. It performs a division operation on two values and returns the remainder.

The last statement shows another form of the assignment with operation (+=) operator. Any time you use the assignment with operation operator, it is the same as applying the binary operator to both the left hand and right hand sides of the operator and putting the results into the left hand side. The example could have been written as

*result = result + x;*and returned the same value.**The Array Type**

Another data type is the Array, which can be thought of as a container that has a list of storage locations for a specified type. When declaring an Array, specify the type, name, dimensions, and size.

**Listing 2-4. Array Operations: Array.cs**

using System;

class Array

{

public static void Main()

{

int[] myInts = { 5, 10, 15 };

bool[][] myBools = new bool[2][];

myBools[0] = new bool[2];

myBools[1] = new bool[1];

double[,] myDoubles = new double[2, 2];

string[] myStrings = new string[3];

Console.WriteLine("myInts[0]: {0}, myInts[1]: {1}, myInts[2]: {2}", myInts[0], myInts[1], myInts[2]);

myBools[0][0] = true;

myBools[0][1] = false;

myBools[1][0] = true;

Console.WriteLine("myBools[0][0]: {0}, myBools[1][0]: {1}", myBools[0][0], myBools[1][0]);

myDoubles[0, 0] = 3.147;

myDoubles[0, 1] = 7.157;

myDoubles[1, 1] = 2.117;

myDoubles[1, 0] = 56.00138917;

Console.WriteLine("myDoubles[0, 0]: {0}, myDoubles[1, 0]: {1}", myDoubles[0, 0], myDoubles[1, 0]);

myStrings[0] = "Joe";

myStrings[1] = "Matt";

myStrings[2] = "Robert";

Console.WriteLine("myStrings[0]: {0}, myStrings[1]: {1}, myStrings[2]: {2}", myStrings[0], myStrings[1], myStrings[2]);

}

}

class Array

{

public static void Main()

{

int[] myInts = { 5, 10, 15 };

bool[][] myBools = new bool[2][];

myBools[0] = new bool[2];

myBools[1] = new bool[1];

double[,] myDoubles = new double[2, 2];

string[] myStrings = new string[3];

Console.WriteLine("myInts[0]: {0}, myInts[1]: {1}, myInts[2]: {2}", myInts[0], myInts[1], myInts[2]);

myBools[0][0] = true;

myBools[0][1] = false;

myBools[1][0] = true;

Console.WriteLine("myBools[0][0]: {0}, myBools[1][0]: {1}", myBools[0][0], myBools[1][0]);

myDoubles[0, 0] = 3.147;

myDoubles[0, 1] = 7.157;

myDoubles[1, 1] = 2.117;

myDoubles[1, 0] = 56.00138917;

Console.WriteLine("myDoubles[0, 0]: {0}, myDoubles[1, 0]: {1}", myDoubles[0, 0], myDoubles[1, 0]);

myStrings[0] = "Joe";

myStrings[1] = "Matt";

myStrings[2] = "Robert";

Console.WriteLine("myStrings[0]: {0}, myStrings[1]: {1}, myStrings[2]: {2}", myStrings[0], myStrings[1], myStrings[2]);

}

}

And here's the output:

myInts[0]: 5, myInts[1]: 10, myInts[2]: 15

myBools[0][0]: true, myBools[1][0]: true

myDoubles[0, 0]: 3.147, myDoubles[1, 0]: 56.00138917

myStrings[0]: Joe, myStrings[1]: Matt, myStrings[2]: Robert

myBools[0][0]: true, myBools[1][0]: true

myDoubles[0, 0]: 3.147, myDoubles[1, 0]: 56.00138917

myStrings[0]: Joe, myStrings[1]: Matt, myStrings[2]: Robert

Listing 2-4 shows different implementations of Arrays. The first example is the

*myInts*Array, which is a single-dimension array. It is initialized at declaration time with explicit values.Next is a jagged array,

*myBools*. It is essentially an array of arrays. We needed to use the*new*operator to instantiate the size of the primary array and then use the*new*operator again for each sub-array.The third example is a two dimensional array,

*myDoubles*. Arrays can be multi-dimensional, with each dimension separated by a comma. It must also be instantiated with the*new*operator.One of the differences between jagged arrays,

*myBools[][]*, and multi-dimension arrays,*myDoubles[,]*, is that a multi-dimension array will allocate memory for every element of each dimension, whereas a jagged array will only allocate memory for the size of each array in each dimension that you define. Most of the time, you'll be using multi-dimension arrays, if you need multiple dimensions, and will only use jagged arrays in very special circumstances when you are able to save significant memory by explicitly specifying the sizes of the arrays in each dimension.Finally, we have the single-dimensional array of

*string*types,*myStrings*.In each case, you can see that array elements are accessed by identifying the integer index for the item you wish to refer to. Arrays sizes can be any

*int*type value. Their indexes begin at 0.
## No comments:

## Post a Comment