Вы находитесь на странице: 1из 56

Storage of Digital Data within a Computer System

Understanding the elements of computer storage will enable a GIS user to design optimum
storage for different types of data.
2.1 Bits

Computers function on two basic elements, on and off.

The smallest processing unit is called a bit (short for Binary digIT). Each bit can have
one of 2 values: "on" (indicated by the value 1) and "off" (indicated by the value 0).

Bits are grouped together in sets of eight, called bytes.

2.2 Binary Systems

Computers use a binary system for storing numbers. In a binary system, the only figures
are 1 and 0.

Binary systems are best explained by comparison to the familiar decimal system. (A
decimal system is uses 10 figures.)
o In a decimal system, the digits 206 represent the number that is made up of
2 lots of 10 plus 0 lots of 10 plus 6 lots of 10
(from high school mathematics: 10 is 100, 10 is 10, and 10 is 1)
o In a binary system the digits 101 represent the number that is made up of:
1 lots of 2 plus 0 lots of 2 plus 1 lot of 2
(2 is 4, 2 is 2, and 2 is 1, so the number is 4 + 1 = 5)

Counting from 1 to 10 in binary gives the following series of numbers:


1, 10, 11, 100, 101, 110, 111, 1000, 1001, 1010

Binary and decimal systems are just 2 number systems: potentially there are many others
that could be used. Two others, octal and hexadecimal, are common because they are also
used in computing. Table1 gives the numbers for counting from 1 to 20 in these systems.

2.3 Bytes

One byte of storage is 8 bits, and so can hold integer numbers in the range 0 to 255.
o (Integer numbers are numbers that don't have decimal points.)
o The number 255 is the limit, because it is the binary number 11111111 (8 1's)
which equals 1 (which is 2), plus 2 (which is 2), plus 4 (2),
plus 8 (2), plus 16, plus 32, plus 64, plus 128.

This is a very useful range of data. Much (but certainly not all) non- spatial data in a GIS,
falls in this range. For example:
1. even in complex forested areas, tree species can usually be allotted a discrete code
within this range.
BUT elevation often falls outside this range.
2. Remote sensing data is designed to fall into this range for ease of transmission
from the sensors in the satellite back to earth.

Integer data with values greater than 255, require more than one byte of storage to be
stored in a computer system. See Storage of Numerical Data

2.4 The ASCII Coding System

The ASCII (pronounced ass-key) coding system is another important use of bytes of data.
The acronym stands for American Standard Code for Information Interchange.

Every letter and number key on a keyboard has a unique code.


o Seven bits are used to give 127 code numbers which are assigned to each key
(upper and lower case letters have different codes).
o There are also codes for special characters such as Tabs and Carriage Returns.
See Table 2 - ASCII codes for Various Keyboard Characters
o The 127 basic ASCII codes can be extended to by using the eigth bit to give a
total of 255 codes. These extra codes include characters for international (nonEnglish) characters such as , mathematical symbols such as , and graphics
characters such as those in table borders.
o Recently a 16-bit international character set, that allows kanji and chinese
characters, has been introduced.

Textual data in a GISystem are stored as ASCII characters. See Storage of Character Data

There are ASCII codes for numbers as well as letters. These codes are completely
different from the binary representation of the numbers.
o The 4 in the name "42nd Street" has the ASCII code 52 which is expressed in
binary as 00110100,
o the number 4 in a binary system is 00000100.

ASCII coding is an important standard for the transfer of data.


o The way numerical data is stored in a computer depends on the architechture of
the computer (see Storage of Numerical Data). Data created on one type of
computer will be mis-interpreted by a computer with a different architechture.
Data should be converted to ASCII before transfer, as all computers correctly
interpret ASCII codes.
o most GISystem software offer "export" options which produce ASCII files.
o the disadvantage to ASCII coding, is that GIS data files are very much larger
coded this way.

2.5 Storage of Numerical Data

The way numerical data is stored in a computer depends on the architechture of the
computer: this depends on the type of computer ("personal" or mainframe) and the age.
The number of bits that the computer uses as the basic unit to store data is called
the word size. For example, the following sizes are commonly used:
o 16-bit (2-bytes) "personal computers" (previous generation)
o 32-bit (4-bytes) "personal computers" (current generation)
o 64-bit (8-bytes) mainframes

Computers store negative numbers by the use of a sign bit.


o The sign bit is usually the high order bit, that is the bit in the left-most position.
o If this bit is set (has the value 1) the number is negative, if it is unset (has the
value 0) the number is positive.

o The software indicates to the computer processor whether the high order bit is to
be treated as a sign bit, or part of the number. (Except for byte data, numeric data
is usually stored as signed data.)

In a GISystem most spatial data, which may be in decimal degrees or UTM coordinates,
will include data with decimal places. In computer systems this is usually called floating
point data.

Each number is stored in two parts:


o the first part (called the mantissa) is the value of the number,
o the second part (called the exponent) is the power of ten (or the power of two in a
binary system) to multiply the mantissa by, to obtain the original number.

This sort of storage is more complex, but it can be illustrated with a fairly simple
example:
The latitude of Sydney in decimal degrees is 33.86167 S.
Using decimal numbers, the numbers used to store this figure would be 3386167 and 2.
o the number 3386167 is the mantissa,
o it is assumed to have a decimal point on its immediate left, so its value becomes .
3386167
o the number 2 is the exponent, because .3386167 must be multiplied by 10 (or
100) to obtain the original number (33.86167).
o (The same steps are used in a binary system, but the numbers are different.)

In general, integer data storage has advantages over floating point storage:
o In choosing a storage type, users should consider, the intended uses of the data
stored in the GISystem, and the types of values that will need to be represented.
o Depending on the GIScience software being used, floating point numbers may
require considerably more storage space than integer numbers, because two
numbers must be stored for every value.
o Similarly floating point numbers are more complex to process.

2.6 Storage of Character Data

Character data stored in a GIS may be single letters or characters (for example * or a
space), single words, or groups of words such as a property owner's name or vegetation
species.

Groups of letters or characters are usually called character strings.

Numbers can be stored as character data. For example it is useful to be able to store lot
numbers for land parcels.
o A number stored as a character string will be stored as a series of characters.
o It is not usually possible to use numbers stored as character strings in
mathematical operations, such as addition.

If a character string includes spaces (the ASCII code 32), it is necessary to use
a terminator to indicate the extent of the string. Different software uses different
terminators, for example some use single quotes (') and others use double quotes (").

Stack and heaps:


What goes inside when you declare a variable?
When you declare a variable in a .NET application, it allocates some chunk of memory in the
RAM. This memory has three things: the name of the variable, the data type of the variable, and
the value of the variable.
That was a simple explanation of what happens in the memory, but depending on the data type,
your variable is allocated that type of memory. There are two types of memory allocation: stack
memory and heap memory. In the coming sections, we will try to understand these two types of
memory in more detail.

Stack and heap

In order to understand stack and heap, lets understand what actually happens in the below code
internally.
Hide Copy Code
public void Method1()
{
// Line 1
int i=4;
// Line 2
int y=2;
//Line 3
class1 cls1 = new class1();
}
Its a three line code, lets understand line by line how things execute internally.

Line 1: When this line is executed, the compiler allocates a small amount of memory in
the stack. The stack is responsible for keeping track of the running memory needed in your
application.
Line 2: Now the execution moves to the next step. As the name says stack, it stacks this
memory allocation on top of the first memory allocation. You can think about stack as a series of
compartments or boxes put on top of each other.
Memory allocation and de-allocation is done using LIFO (Last In First Out) logic. In other words
memory is allocated and de-allocated at only one end of the memory, i.e., top of the stack.

Line 3: In line 3, we have created an object. When this line is executed it creates a
pointer on the stack and the actual object is stored in a different type of memory location called
Heap. Heap does not track running memory, its just a pile of objects which can be reached at
any moment of time. Heap is used for dynamic memory allocation.
One more important point to note here is reference pointers are allocated on stack. The
statement, Class1 cls1; does not allocate memory for an instance of Class1, it only allocates a
stack variable cls1 (and sets it tonull). The time it hits the new keyword, it allocates on "heap".
Exiting the method (the fun): Now finally the execution control starts exiting the method.
When it passes the end control, it clears all the memory variables which are assigned on stack. In
other words all variables which are related to int data type are de-allocated in LIFO fashion
from the stack.
The big catch It did not de-allocate the heap memory. This memory will be later de-allocated
by the garbage collector.

Now many of our developer friends must be wondering why two types of memory, cant we just
allocate everything on just one memory type and we are done?
If you look closely, primitive data types are not complex, they hold single values like int i = 0.
Object data types are complex, they reference other objects or other primitive data types. In other
words, they hold reference to other multiple values and each one of them must be stored in
memory. Object types need dynamic memory while primitive ones needs static type memory. If
the requirement is of dynamic memory, its allocated on the heap or else it goes on a stack.
Image taken from http://michaelbungartz.wordpress.com/

Value types and reference types


Now that we have understood the concept of Stack and Heap, its time to understand the concept
of value types and reference types. Value types are types which hold both data and memory on
the same location. A reference type has a pointer which points to the memory location.
Below is a simple integer data type with name i whose value is assigned to another integer data
type with namej. Both these memory values are allocated on the stack.

When we assign the int value to the other int value, it creates a completely different copy. In
other words, if you change either of them, the other does not change. These kinds of data types
are called as Value types.

When we create an object and when we assign an object to another object, they both point to the
same memory location as shown in the below code snippet. So when we assign obj to obj1, they
both point to the same memory location.
In other words if we change one of them, the other object is also affected; this is termed as
Reference types.

So which data types are ref types and which are value types?
In .NET depending on the data type, the variable is either assigned on the stack or on the heap.
String and Objects are reference types, and any other .NET primitive data types are assigned
on the stack. The figure below explains the same in a more detail manner.

Moving Data from Stack to Heap:


Boxing and unboxing
Wow, you have given so much knowledge, so whats the use of it in actual programming? One of
the biggest implications is to understand the performance hit which is incurred due to data
moving from stack to heap and vice versa.
Consider the below code snippet. When we move a value type to reference type, data is moved
from the stack to the heap. When we move a reference type to a value type, the data is moved
from the heap to the stack.
This movement of data from the heap to stack and vice-versa creates a performance hit.
When the data moves from value types to reference types, it is termed Boxing and the reverse is
termed UnBoxing.

If you compile the above code and see the same in ILDASM, you can see in the IL code how
boxing and unboxing looks. The figure below demonstrates the same.

Performance implication of boxing and unboxing

In order to see how the performance is impacted, we ran the below two functions 10,000 times.
One function has boxing and the other function is simple. We used a stop watch object to
monitor the time taken.
The boxing function was executed in 3542 ms while without boxing, the code was executed in
2477 ms. In other words try to avoid boxing and unboxing. In a project where you need boxing
and unboxing, use it when its absolutely necessary.
With this article, sample code is attached which demonstrates this performance implication.

Currently I have not included source code for unboxing but the same holds true for it.
You can write code and experiment it using the stopwatch class.

Numeric Data Types (Visual Basic)


Visual Basic supplies several numeric data types for handling numbers in various
representations. Integral types represent only whole numbers (positive, negative, and zero),
and nonintegral types represent numbers with both integer and fractional parts.
For a table showing a side-by-side comparison of the Visual Basic data types, see Data Type
Summary (Visual Basic).
Integral Numeric Types
Integral data types are those that represent only numbers without fractional parts.
The signed integral data types are SByte Data Type (Visual Basic) (8-bit), Short Data Type
(Visual Basic) (16-bit), Integer Data Type (Visual Basic) (32-bit), and Long Data Type (Visual

Basic) (64-bit). If a variable always stores integers rather than fractional numbers, declare it as
one of these types.
The unsigned integral types are Byte Data Type (Visual Basic) (8-bit), UShort Data Type (Visual
Basic) (16-bit), UInteger Data Type (32-bit), and ULong Data Type (Visual Basic) (64-bit). If a
variable contains binary data, or data of unknown nature, declare it as one of these types.
Performance
Arithmetic operations are faster with integral types than with other data types. They are fastest
with the Integer and UInteger types in Visual Basic.
Large Integers
If you need to hold an integer larger than the Integer data type can hold, you can use
the Long data type instead. Long variables can hold numbers from -9,223,372,036,854,775,808
through 9,223,372,036,854,775,807. Operations with Long are slightly slower than with Integer.
If you need even larger values, you can use the Decimal Data Type (Visual Basic). You can hold
numbers
from
-79,228,162,514,264,337,593,543,950,335
through
79,228,162,514,264,337,593,543,950,335 in a Decimalvariable if you do not use any decimal
places. However, operations with Decimal numbers are considerably slower than with any other
numeric data type.
Small Integers
If you do not need the full range of the Integer data type, you can use the Short data type, which
can hold integers from -32,768 through 32,767. For the smallest integer range, the SByte data
type holds integers from -128 through 127. If you have a very large number of variables that hold
small
integers,
the
common
language
runtime
can
sometimes
store
your Short and SByte variables more efficiently and save memory consumption. However,
operations with Short and SByte are somewhat slower than with Integer.
Unsigned Integers
If you know that your variable never needs to hold a negative number, you can use the unsigned
types Byte, UShort, UInteger, and ULong. Each of these data types can hold a positive integer
twice as large as its corresponding signed type (SByte, Short, Integer, and Long). In terms of
performance, each unsigned type is exactly as efficient as its corresponding signed type. In
particular, UInteger shares with Integer the distinction of being the most efficient of all the
elementary numeric data types.

Nonintegral Numeric Types


Nonintegral data types are those that represent numbers with both integer and fractional parts.
The nonintegral numeric data types are Decimal (128-bit fixed point), Single Data Type (Visual
Basic) (32-bit floating point), and Double Data Type (Visual Basic) (64-bit floating point). They
are all signed types. If a variable can contain a fraction, declare it as one of these types.
Decimal is not a floating-point data type. Decimal numbers have a binary integer value and an
integer scaling factor that specifies what portion of the value is a decimal fraction.
You can use Decimal variables for money values. The advantage is the precision of the values.
The Double data type is faster and requires less memory, but it is subject to rounding errors.
The Decimal data type retains complete accuracy to 28 decimal places.
Floating-point (Single and Double) numbers have larger ranges than Decimal numbers but can
be subject to rounding errors. Floating-point types support fewer significant digits
than Decimal but can represent values of greater magnitude.
Nonintegral number values can be expressed as mmmEeee, in which mmm is the mantissa (the
significant digits) and eee is the exponent (a power of 10). The highest positive values of the
nonintegral types are 7.9228162514264337593543950335E+28 for Decimal, 3.4028235E+38
for Single, and 1.79769313486231570E+308 for Double.
Performance
Double is the most efficient of the fractional data types, because the processors on current
platforms perform floating-point operations in double precision. However, operations
with Double are not as fast as with the integral types such as Integer.
Small Magnitudes
For numbers with the smallest possible magnitude (closest to 0), Double variables can hold
numbers as small as -4.94065645841246544E-324 for negative values and
4.94065645841246544E-324 for positive values.
Small Fractional Numbers
If you do not need the full range of the Double data type, you can use the Single data type,
which can hold floating-point numbers from -3.4028235E+38 through 3.4028235E+38. The
smallest magnitudes forSingle variables are -1.401298E-45 for negative values and 1.401298E45 for positive values. If you have a very large number of variables that hold small floating-point

numbers, the common language runtime can sometimes store your Single variables more
efficiently and save memory consumption.
Character Data Types (Visual Basic)
Visual Basic provides character data types to deal with printable and displayable characters.
While they both deal with Unicode characters, Char holds a single character
whereas String contains an indefinite number of characters.
For a table that displays a side-by-side comparison of the Visual Basic data types, see Data Type
Summary (Visual Basic).
Char Type
The Char data type is a single two-byte (16-bit) Unicode character. If a variable always stores
exactly one character, declare it as Char. For example:
VB
' Initialize the prefix variable to the character 'a'.
Dim prefix As Char = "a"
Each possible value in a Char or String variable is a code point, or character code, in the
Unicode character set. Unicode characters include the basic ASCII character set, various other
alphabet letters, accents, currency symbols, fractions, diacritics, and mathematical and technical
symbols.

Note

The Unicode character set reserves the code points D800 through DFFF (55296 through 55551 decimal) for su
A Char variable cannot hold a surrogate pair, and a String uses two positions to hold such a pair.

For more information, see Char Data Type (Visual Basic).


String Type
The String data type is a sequence of zero or more two-byte (16-bit) Unicode characters. If a
variable can contain an indefinite number of characters, declare it as String. For example:
VB

' Initialize the name variable to "Monday".


Dim name As String = "Monday"
Miscellaneous Data Types (Visual Basic)

Visual Basic supplies several data types that are not oriented toward numbers or characters.
Instead, they deal with specialized data such as yes/no values, date/time values, and object
addresses.
For a table showing a side-by-side comparison of the Visual Basic data types, see Data Type
Summary (Visual Basic).
Boolean Type
The Boolean Data Type (Visual Basic) is an unsigned value that is interpreted as
either True or False. Its data width depends on the implementing platform. If a variable can
contain only two-state values such as true/false, yes/no, or on/off, declare it as Boolean.
Date Type
The Date Data Type (Visual Basic) is a 64-bit value that holds both date and time information.
Each increment represents 100 nanoseconds of elapsed time since the beginning (12:00 AM) of
January 1 of the year 1 in the Gregorian calendar. If a variable can contain a date value, a time
value, or both, declare it as Date.
Object Type
The Object Data Type is a 32-bit address that points to an object instance within your application
or in some other application. An Object variable can refer to any object your application
recognizes, or to data of any data type. This includes both value types, such as Integer, Boolean,
and structure instances, and reference types, which are instances of objects created from classes
such as String and Form, and array instances.
If a variable stores a pointer to an instance of a class that you do not know at compile time, or if
it can point to data of various data types, declare it as Object.
The advantage of the Object data type is that you can use it to store data of any data type. The
disadvantage is that you incur extra operations that take more execution time and make your
application perform slower. If you use an Object variable for value types, you
incur boxing and unboxing. If you use it for reference types, you incur late binding.

Understand Computer Decision Structures

Control Structures (loops, ifs, and switch)


Welcome to my tutorial on Control Structures (or constructs for short). In this tutorial, we shall
go through each control structure in turn, and then we shall finish by demonstrating them in use
with a basic example.
Why is this important to learn about?
Control structures are one of the most fundamental concepts in any programming language you
will come across. If you want to know C#, and if you want to learn to program with any
language, you must have a firm grip on the control structures of the language.
Generally speaking, if you learn them in one language, you will pick them up in different
languages very quickly.
Definitions of terms used.
Iteration This is the act of repeating something (that something being code statements in the
programming context).
Conditional A conditional action is said to be one that is only performed if a certain condition
is true.
Note: All examples were created using Visual Studio 2010, targetting the .NET Framework 4.0.
We'll do our best to point out anything that might not work in older versions.
Control Structures (loops, ifs, and switch)
Before we begin, you should have a reasonable understanding of:

Data Types
Operators

A basic knowledge of methods would be useful, but is not required.


Right let's get started!

C# has two general types of control structures:

Iteration (often called 'loops')


Decision (sometimes called 'conditionals', or 'selection statements')

Note that all examples are written in a ConsoleApplications Main() method.


Iteration Constructs
Iteration constructs (otherwise known as loops) allow us to repeat code in a cyclic fashion. There
are 4 types of iteration constructs in C#, each of which are covered next.
For Loops
For loops allow us to specify the number of times to repeat a block of code. It is best
demonstrated with an example:
1
for (int i = 1; i <= 5; i++)
2
{
3
Console.WriteLine(i);
4
}
This basic example prints out all the numbers from 1 to 5. How does it do this? I shall go through
what happens when the loop executes, step by step:
1. An integer variable called i is declared and set equal to 1 in this line; int i = 1. This is called
the initialisation variable.
2. The condition i <= 5 is checked. If i is less than or equal to 5, then the body of the loop is
executed. Otherwise, the loop terminates. 1 is less than 5, so the condition evaluates to true, and
the body (the code statements in the curly braces) of the loop executes.
3. Once the body has been executed once (and the value of i has been printed out, which, of
course, is currently 1). Next, this statement is executed; i++. This means increment the value of
i by 1. Consequently meaning that i now equals 2.
4. Now, the condition i <= 5 is evaluated again. i is now equal to 2, of which is less than 5, so
the loop body executes again, printing out 2.
5. i++ is then executed, meaning i is now 3. i <= 5 evaluates to true as 3 is less than 5, so the
3 gets printed out to the console.

6. i++ is executed again, meaning i equals 4, which is still less than 5, so the loop body
executes again, leading to 4 being printed.
7. i++ is executed once again, meaning i equals 5. i <= 5 is testing if i is less than OR equal to
5. i is now equal to 5, so it still evaluates to true, meaning the loop body is executed again, thus
printing out 5.
8. i++ is executed again, meaning i now equals 6. Now, i <= 5 evaluates to false. Therefore, the
loop terminates as the terminating condition has evaluated to false. The loop doesnt get executed
any more, and the program can continue to execute statements that come after the loop.
Thus, we see numbers from 1 to 5 printed out to the console in this example.
Note that you can use any condition as the terminating condition, as long as it evaluates to true of
false
/>.
A few points to note:

The i++ statement can be changed to any other valid statement that modifies the variable
i. A few examples include; i--(decrement the variable by 1), i += 2 (add 2 to the variable)
etc.

In our example, the scope of the variable i is limited to the body of the for loop. So, if
we did this:

1
2
3
4
5
6

for (int i = 1; i <= 5; i++)


{
Console.WriteLine(i);
}
Console.WriteLine(i); //i is not available here

, we would get an error saying i does not exist in the current context. This is because i is not
available outside of the for loop.
To get around this, we can declare the initialisation variable (i), before the for loop, like this:
1
2
3
4

int i;
for (i = 1; i <= 5; i++)
{

5
6
7
8

Console.WriteLine(i);
}
Console.WriteLine(i);

We declare i before the loop. This is perfectly valid as i is now not limited to within the for
loop. This code will print out numbers 1 6. The loop prints out numbers 1 5, then the line
after the for loop will print out the value of i, which, by that point, will be 6, due to the how the
for loop increments i with this statement; i++.

1
2
3
4
5

Finally, you can have multiple initialisation variables in a single for loop. This example
doesnt produce any meaningful output, but it demonstrates the idea:
for (int i = 1, j = 6; i <= 5; i++, j--)
{
Console.WriteLine(i);
Console.WriteLine(j);
}

Notice how we use the comma , to declare two int variables; i and j. We can still only use
one condition in the for loop. We then have two statements (again, separated by a comma). One
that increments i, and once that decrements j.
You can use this principle to build some quite complex for loops!
Here is an example for you to look at. It prints out the 6 times table, all the way up to 6 times 12.
Can you see how it works?
1
for (int i = 6, j = 0; j <= 12; j++)
2
{
3
Console.WriteLine(i*j);
4
}
Foreach Loops
Foreach loops (sometimes called foreach, in loops) are used to loop through items in an array
or a collection. The collection/array must implement an interface
called IEnumerable (or IEnumerable<T> if the collection is generic). This interface will be
covered in the collections tutorial later in this series. For now, it is enough to say it is a way to
ensure that an enumerator can be obtained for the collection, of which is needed to allow
iteration through the collection.

Here is an example of its use:


1
2
3
4
5
6
7
8
9

//declare an array to hold some numbers


int[] array = new int[] { 1, 5, 3, 8, 3, 5, 8, 6, 10 };
//foreach loop
foreach (int i in array)
{
//this gets executed once for each number in the array
Console.WriteLine(i);
}

We first declare an array of numbers. Arrays do implement IEnumerable, and so we can use a
Foreach loop to loop through each number in the array, one at a time, executing the body of the
loop (the code in between the curly braces) 'for each' element.
The variable i in the above is the variable that holds the current element/number, and is of type
int because the elements in our array are of type int. On the first loop through, i is 1, the second,
it is 5 and so on until we reach the end of our array.
In this example, we print each number in the array to the console using the Console.WriteLine()
method.
The important thing to recognise with foreach loops is that you cannot add to, or remove from,
the collection from within the loop body.
Any attempt to add to, or remove from, the collection will lead to
an InvalidOperationException that reads something like this:

Quote
Collection was modified; enumeration operation may not execute.
If you need to add to, or remove from, the collection, use a for loop.
While Loop
The while loop is used to repeat a block of code, while a certain condition remains true.
It is similar in concept to a for loop, and they can be practically used interchangeably. However,
using a certain type over another can make your life easier in certain situations. For example,
when you know exactly how many iterations you need, a for loop will be the easiest loop to use.

Having said that, you could equally use a while loop, with a little bit of extra code, to loop a
certain number of times.
It's about making a choice based on your situation

/>.

Here is an example:
1 Console.WriteLine("Enter 'Q' to quit the while loop. Enter anything else to loop again");
2
3 while (Console.ReadLine().ToUpper().Equals("Q") == false)
4 {
5
Console.WriteLine("Still in while loop");
6 }
7
8 Console.WriteLine("While loop ended");
Here, we first print out a message telling the user what to do. Then, we get to the while loop.
Inside its parentheses () goes the Boolean condition to test. It will continue looping until the
condition inside the parentheses evaluates to false.
What is happening with that condition?
We read it from left to right. Firstly, Console.ReadLine() reads what the user has entered, and
then ToUpper() converts it to upper case. We then use Equals() to compare what the user has
entered to Q. If they are equal, Equals() will return true, otherwise, it will return false.
Now, we want to continue looping until the user enters Q, therefore, we want to loop while the
input isnt equal to Q. Therefore, we compare the value returned by Equals() to false. If
Equals() returns false, continue looping as the user didnt enter Q. If it returns true, then we
dont want to continue looping, as the user has signalled that they have finished by entering 'Q'.
On every loop, we display a message saying that we are still in the while loop.
Once the loop exits because the user entered Q, we print a message alerting the user to this
fact. Just so you can see that the loop has terminated.
Do While
Now, imagine that we have written our while loop as above, and it is working fine. However, you
then realise that if the user starts up the program, and then enters Q straight away, the loop
wont execute at all! Therefore, your message; Still in while loop wont get displayed at all.
You want the message to be displayed at least once.

How do we do that then? Well, for the message to be displayed at least once, the loop body needs
to execute at least once. We need some way of ensuring that at least one loop cycle is executed.
Enter the do while loop!
To ensure the message is displayed at least once in the above while loop example, we could
change it to this:
1
2
3
4
5

do{
Console.WriteLine("Still in while loop");
} while (Console.ReadLine().ToUpper().Equals("Q") == false); //remember the semi-colon
here :)/>/>

6
7

Console.WriteLine("While loop ended");

Notice how we use the do keyword to show the block that we want repeating (the loop body),
and notice how the while() condition comes after the body of the loop. The affect of this is that
the message Still in while loop is always shown at least once, even if the first thing the user
enters is "Q".
When the user starts up the program, the message Still in while loop will be displayed as the
body of the while loop will execute once, andonly then will it check the condition in the
parentheses to check if the user has entered "Q".
This makes the do while loop ideal for situation when you need to guarantee that the body of the
loop is executed at least once.

Decision Constructs
Decision constructs are used to control the flow of our programs. They allow us to run certain
blocks of code once, only if a certain condition is true.
There are two key types of decision constructs in C#:

if/else/else if
switch

If Statements
As suggested, the if statements come in various different forms, each of which is described
below:
if statement This is used to execute some code ONCE, only when a specified condition is true.
if...else statement This is an extension to the standard if statement. It allows to specify the
code that should run, if the condition in the first if statement evaluates to false.
if...else if....else statement This is used to select from multiple different blocks of code. It is
often very similar to the switch statement which we shall cover soon.
If statements allow you to choose which code gets executed.
Here are some examples to demonstrate each version:
We first get the user to enter an integer:
1 Console.WriteLine("Enter a number");
2
3 //this line just tries to convert the string entered into a number
4 int i = int.Parse(Console.ReadLine());
Now, we could display a message if the number entered is less than 10:
1
2
3
4

if (i < 10)
{
Console.WriteLine("Number is less than 10!");
}

Simple enough, right? If (and only if) i is less than 10, display the message.
However, if i is not less than 10, the user gets no message. This isnt very friendly.
To get around this, we need to display a message if the number entered is NOT less than 10. To
do this, we can use an else block:
1
2

if (i < 10)
{

3
Console.WriteLine("Number is less than 10!");
4 }
5
else
6
{
7
Console.WriteLine("Number is NOT less than 10!");
8 }
This ensures that a message will always be displayed if the user enters a valid integer. If it is less
than 10, the first message is displayed; else the other message is displayed.
Note that you can combine multiple conditions in a single if statement by using the && and ||
operators. They are read as 'and' and 'or' respectively. For example, you could do this to execute
code only when the number entered is below 20, but greater than 10:
1
2
3
4

if(i < 20 && i > 10)


{
//code to excecute here
}

It is important to realise that we cannot write this:


1
2
3
4

if(i < 20 && 10)


{
//code to execute here
}

We must exlicitly state the variable we are comparing against for every condition. You can use as
many '&&' and '||' operators as you like in a single if statement. You can also combine the two.
1
2
3
4

if( (i < 20 && i > 10) || (i < 30 && i > 20) )


{
//code to execute here
}

This can be read as; "if 'i' is less than 20 and greater than 10, OR, if 'i' is less than 30 and greater
than 20", execute this code.
These two operators work a 'short circuit' system. If the first expression in an 'or' condition
evaluates to true, there is no point in executing the second expression as we know the 'or'
condition as a whole is going to evaluate to true, as only one expression needs to be true in an 'or'
condition to make the whole condition true.

Likewise, if the first expression in an 'and' condition evaluates to false, as both expressions have
to be true for an && expression to be true, we already know that the condition is going to
evaluate to false without even looking at the second expression.
You should have already covered the different operators in an earlier tutorial

/>.

Now, moving onto the last variation of if statement...


Heres an example of the most complex standalone if statement you will see:
01
if (i == 10)
02
{
03
Console.WriteLine("Number is 10!");
04
}
05
else if (i == 20)
06
{
07
Console.WriteLine("Number is 20!");
08
}
09
else if (i == 30)
10
{
11
Console.WriteLine("Number is 30!");
12
}
13
else
14
{
15
Console.WriteLine("Number is NOT 10,20 or 30");
16 }

This first checks if the number is 10, if it is, it displays the first message, else, if the number is
20, it displays the second message etc. If the number isnt 10, 20 or 30, we shall drop all the way
down to the else statement, where another message will be displayed, telling the user that the
number wasnt 10, 20 or 30.
NOTE: The expression (the condition that is being tested) in the parentheses of if statements
needs to be a Boolean. In other languages like C++ and PHP, you can just put numbers on their
own in if statements and they are converted automatically to Boolean values (0 is false,
everything else is true). You cannot do that in C#. The condition has to be able to be directly
evaluated as a Boolean (true or false).
Also, you can compare strings, objects etc in if statements. You are by no means limited to
numbers!

Switch Statement
Now, the last example I showed (the if/else if/else) can be written in a clearer manner using
something called a switch statement.
A switch statement allows use to execute code based on a predefined set choices.
Here is the previous example written as a switch statement:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16

switch (i)
{
case 10:
Console.WriteLine("Number is 10!");
break;
case 20:
Console.WriteLine("Number is 20!");
break;
case 30:
Console.WriteLine("Number is 30!");
break;
default:
Console.WriteLine("Number is NOT 10,20 or 30");
break;
}

The value we want to compare with our predefined values is placed in the parentheses of the
switch, and must be a String or an Integral type. Then, we list our different choices (or cases),
which, unlike with the if statement, must each be a constant expression. This does exactly the
same thing as the if/else if statement above. If 'i' is 10, execute the 'case 10:' block, if it is 20,
execute the 'case 20:' block etc.
Notice the use of default. This specifies the default case to be executed if none of the other
cases match the input. It is equivalent to an else statement. The default case is optional, but the
use of the break keyword is not.
The break keyword is a terminating statement. Without it, something called fall-through would
occur.
Say the user enters 10, meaning the first case will be satisfied and the message Number is 10!"
will be displayed. Without the break statement, all the other messages would also be displayed
also. We would fall through and execute the remaining cases too.

To avoid this, the C# compiler demands that you use the break keyword on every case; even
the default case.
If you miss a break statement for case 10 in the above example, you will see an error message
like this:

Quote
Control cannot fall through from one case label ('case 10:') to another
Note: you can also use the return keyword instead of the break keyword to prevent fall
through. The reason you would use the return keyword is if you wanted to return a value from a
method using a switch statement to decide what value to return.
Continue and Break Keywords
We have already met the break keyword in the context of the switch statement. However, these
keywords are more often used in the iterations constructs (loop) we covered earlier. They are
usually used in conjunction with the if statement constructs, to manipulate loops when certain
conditions are met.
The continue keyword is used to instantly move to the next iteration of a loop body.
We could use it to print out all the multiples of 5 between 1 and 20 for example:
1
2
3
4
5
6
7
8
9

for (int i = 1; i <= 20; i++)


{
if (i % 5 != 0)
{
continue;
}
Console.WriteLine(i);
}

I am sure that you can see, this for loop starts with i equal to 1, it executes the body of the loop
(in between the curly braces), and increments the variable i, until the variable i equals 21, at
which point, it stops looping.
Now, on every loop, the condition in the if statement is tested. What it does is it divides the
current value of i by 5, and if it is exactly divisible (i.e. its a multiple of 5), it returns 0.

Therefore, if i % 5 does not equal 0, it means the current value of i isnt a multiple of 5. i % 5 !
= 0 will evaluate to true (remember that != is the NOT EQUAL TO operator) and the continue
statement will be executed. This skips to the next iteration, totally skipping the
Console.WriteLine(i) statement, as we dont want to print i if it isnt a multiple of 5.
Here is what is happening for the first couple of loops:
i begins as being equal to 1, and so i % 5 != 0 is true as 1 isnt a multiple of 5. Therefore, the
continue statement is executed. As soon as the continue statement is executed, i++ is executed,
and the next loop begins, starting at the if statement again. Thus meaning that
Console.WriteLine(i) is completely skipped.
When i equals 5, for example, i % 5 != 0 evaluates to false, as 5 is a multiple of 5 (obviously!),
meaning the continue statement is NOT executed. Therefore, the number is printed out with
Console.WriteLine(i).
Basically, when you see a continue statement, read it as start the next iteration
(loop) immediately, and skip any other code in the loops body that may come after the continue
statement.
The break keyword, when used in the context of loops, can be read as break out of this
loop immediately, and do not loop any more.
If we wrote this, for example:
1
2
3
4
5
6
7
8
9

for (int i = 1; i <= 20; i++)


{
if (i == 10)
{
break;
}
Console.WriteLine(i);
}

Console.WriteLine(The loop has finished executing);


, we would see numbers 1 -9 printed to the console, followed by the message; The loop has
finished executing.
This is because when i equals 10, the if statement condition i == 10 evaluates to true, meaning
the break statement is executed, and execution immediately breaks out of the loop and drops to
the code after the loop, without finishing the current loop.

Further, as with the switch statement, a return statement will cause the loop to terminate also,
but you can return a value from a method at the same time using return.
(NOTE: Technically, there is another statement through which a loop can terminate, and that is
via the use of the throw keyword. Youll get to that in the Exception Handling tutorial
/>).
An example that combines different constructs
In this example, I am going to write a program to calculate the average of list of numbers that the
user enters. It is a very around the houses approach, but it illustrates many of the constructs
explained in this tutorial in use.
Here is the code with some detailed comments:
01 //declare a do-while loop as we want the user to always be able to
02 //calculate the average AT LEAST once. A do-while loop ensures the loop
03
//is executed at least once
04
do
05 {
06
//declare a list collection that will hold the numbers the user enters
07
List<int> numbers = new List<int>();
08
09
//ask user how many numbers they plan to enter
10
Console.WriteLine("How many numbers are you going to enter");
11
12 //capture the number in a variable (after converting it to an integer of course ;)/>/>)
13
int noOfValues = int.Parse(Console.ReadLine());
14
15
//ask user to enter their numbers
16
Console.WriteLine("Enter your numbers:");
17
18
//a loop used to fill our list collection with the numbers
19
//the user enters on number per iteration, and it gets added to the
20
//list. We know how many numbers to expect, so we know how many
21
//iterations we need, making a for loop an ideal choice
22
for (int i = 0; i < noOfValues; i++)
23
{
24
//capture the number, and add it to the list
25
numbers.Add(int.Parse(Console.ReadLine()));
26
}
27

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

//variable to hold the sum of all the integers in the list


int sum = 0;
//the list collection implements IEnumerable<T>, and so
//we can use a Foreach loop to go through each number in
//the list, one by one. Rememeber, we cannot add to, or remove from,
//the collection in a Foreach loop. In this example, we need to
//go through every number in the list, and add each one to a running
//total, making Foreach the ideal choice of loop
foreach (int num in numbers)
{
//add current number to the running total 'sum' variable
sum += num;
}

//calculate average by dividing 'sum' by the number of elements in the list accessed via
the 'Count' property
44 //print the value out
45
Console.WriteLine("Average: {0}", sum/numbers.Count);
46
47
//ask user if they want to enter another list of numbers
48
Console.WriteLine("Do you want to go again - {0} or {1}", "Y", "N");
49
50
//capture whether or not hte user wants to continue in a Boolean variable
51
//by determining whether input was "Y" or not
52
bool again = Console.ReadLine().ToUpper().Equals("Y");
53
54
//check if 'again' is true
55
//notice we don't need to put "again == true"
56
//the exp<b></b>ression is automatically evaluated to true or false
57
if (again)
58
{
59
//if the user wants to go again,
60
//skip everything beyond this line, and start a new iteration
61
//when this is executed, the user will see "How many numbers are you going to enter"
62
//printed in the console as a new loop has begun
63
continue;
64
}
65
else
66
{
67
//else, break out of the loop as the user
68
//doesn't want to enter another list.
43

69
//The user will see "Thank you for using this program"
70
//displayed when this line executes as the is terminated immediately
71
//and execution resumes with the first statement after the loop
72
break;
73
}
74
75
76 } while (true); //notice the use of true here. While loops execute until the condition
77
//in the parentheses is false. However, this will never happen here, so
78
//our loop will loop for ever. It is an infinite loop. This kind of loop
//will usually cause your program to become unresponsive. However, as we have
79
provided a
//'way out' of the loop by using the 'break' keyword, our program will execute
80
fine and
//remain responsive. We didn't strictly need to use an infinity loop here, as we
81
could
//just test this line Console.ReadLine().ToUpper().Equals("Y") in the while's
82
parentheses.
83
//I used an infinity loop so that I could demonstrate the break keyword in action.
84
85
//The for loop equivalent of an infinite loop is:
86
/* for(;;)/>/>
87
{
88
89
}
90
*/
91
92
93 //print out thank you message once user has done
94 Console.WriteLine("Thank you for using this program");

That example demonstrates another point. You can nest all the control structures within each
other. You can have a for loop, of which contains another for loop, of which contains a while
loop, for example. When nesting control structures, you have to be careful with variable scope by
remembering that variables declared in the contruct definition or construct body are NOT
available outside the construct.
The 'sum' variable isn't available outside the while loop in the above example, for example.

In Conclusion

So there you have it! They are the fundamental constructs in the C# language.
Here is a link to the MSDN documentation on statements in general. The three statement types
we are concerned with in this tutorial are the selection statements, the Iteration Statements
and the Jump Statements.
MSDN Reference
Further, there are actually two small, sub features that I didnt mention in the tutorial.
The first thing is that there is actually another type of conditional, decision making construct that
uses a ternary operator (an operator that has 3 operands). It is merely a shorthand for an if/else
construct, and can be handy in certain circumstances, but it is by no means mandatory that you
learn it. Standard if statements can easily suffice, it's just saves a few lines of code ocassionally.
Read about it here:
Ternary Operator
The second thing is the goto statement and label identifiers. I deliberately didnt mention these
as it is strongly advised that you do not use them in real code. There is no situation where
you need these statements to complete a programming task.

goto
The goto statement transfers the program control directly to a labeled statement. It takes one of
the following forms:
goto identifier;
goto case constant-expression;
goto default;
where:
identifier
A label.
constant-expression

A switch-case label.
Remarks
In the first form, the identifier indicates a label located in the current body, the same lexical
scope, or an enclosing scope of the goto statement.
A common use of goto is to transfer control to a specific switch-case label or the default label in
a switch statement.
The goto statement is also useful to get out of deeply nested loops.
A warning message may be issued if the label has never been referenced in the program. For
more information on labels, see 3.3 Declarations.
Example
For an example of using goto to transfer control to a specific switch-case label, see
the switch example.
Example
The following example demonstrates using goto to break out from nested loops.
// statements_goto.cs
// Nested search loops
using System;
public class GotoTest1
{
public static void Main()
{
int x = 200, y = 4;
int count = 0;
string[,] myArray = new string[x,y];
// Initialize the array:
for (int i = 0; i < x; i++)
for (int j = 0; j < y; j++)
myArray[i,j] = (++count).ToString();
// Read input:
Console.Write("Enter the number to search for: ");
// Input a string:
string myNumber = Console.ReadLine();
// Search:

for (int i = 0; i < x; i++)


for (int j = 0; j < y; j++)
if (myArray[i,j].Equals(myNumber))
goto Found;
Console.WriteLine("The number {0} was not found.", myNumber);
goto Finish;
Found:
Console.WriteLine("The number {0} is found.", myNumber);
Finish:
Console.WriteLine("End of search.");
}

Activity 3 - Identify the Appropriate Method for Handling Repetition

Iteration Statements (C# Reference)


You can create loops by using the iteration statements. Iteration statements cause embedded
statements to be executed a number of times, subject to the loop-termination criteria. These
statements are executed in order, except when a jump statement is encountered.
The following keywords are used in iteration statements:

do

for

foreach

in

while

do (C# Reference)
The do statement executes a statement or a block of statements repeatedly until a specified
expression evaluates to false. The body of the loop must be enclosed in braces, {}, unless it
consists of a single statement. In that case, the braces are optional.

Example
In the following example, the do-while loop statements execute as long as the variable x is less
than 5.
C#
public class TestDoWhile
{
public static void Main ()
{
int x = 0;
do
{
Console.WriteLine(x);
x++;
} while (x < 5);
}
}
/*
Output:
0
1
2
3
4
*/
Unlike the while statement, a do-while loop is executed one time before the conditional
expression is evaluated.
At any point in the do-while block, you can break out of the loop using the break statement. You
can step directly to the while expression evaluation statement by using the continue statement. If
the while expression evaluates to true, execution continues at the first statement in the loop. If
the expression evaluates to false, execution continues at the first statement after the dowhile loop.
A do-while loop can also be exited by the goto, return, or throw statements.

for (C# Reference)


By using a for loop, you can run a statement or a block of statements repeatedly until a specified
expression evaluates to false. This kind of loop is useful for iterating over arrays and for other
applications in which you know in advance how many times you want the loop to iterate.

Example
In the following example, the value of i is written to the console and incremented by 1 during
each iteration of the loop.
C#
class ForLoopTest
{
static void Main()
{
for (int i = 1; i <= 5; i++)
{
Console.WriteLine(i);
}
}
}
/*
Output:
1
2
3
4
5
*/
The for statement in the previous example performs the following actions.
1. First, the initial value of variable i is established. This step happens only once, regardless
of how many times the loop repeats. You can think of this initialization as happening
outside the looping process.
2. To evaluate the condition (i <= 5), the value of i is compared to 5.
o

If i is less than or equal to 5, the condition evaluates to true, and the following
actions occur.
a. The Console.WriteLine statement in the body of the loop displays the
value of i.
b. The value of i is incremented by 1.
c. The loop returns to the start of step 2 to evaluate the condition again.

If i is greater than 5, the condition evaluates to false, and you exit the loop.

Note that, if the initial value of i is greater than 5, the body of the loop doesn't run even once.
Every for statement defines initializer, condition, and iterator sections. These sections usually
determine how many times the loop iterates.
C#
for (initializer; condition; iterator)
body
The sections serve the following purposes.

The initializer section sets the initial conditions. The statements in this section run only
once, before you enter the loop. The section can contain only one of the following two
options.
o

The declaration and initialization of a local loop variable, as the first example
shows (int i = 1). The variable is local to the loop and can't be accessed from
outside the loop.

Zero or more statement expressons from the following list, separated by commas.

assignment statement

invocation of a method

prefix or postfix increment expression, such as ++i or i++

prefix or postfix decrement expression, such as --i or i--

creation of an object by using new

await expression

The condition section contains a boolean expression thats evaluated to determine


whether the loop should exit or should run again.

The iterator section defines what happens after each iteration of the body of the loop. The
iterator section contains zero or more of the following statement expressions, separated
by commas:
o

assignment statement

invocation of a method

prefix or postfix increment expression, such as ++i or i++

prefix or postfix decrement expression, such as --i or i--

creation of an object by using new

await expression

The body of the loop consists of a statement, an empty statement, or a block of


statements, which you create by enclosing zero or more statements in braces.
You can break out of a for loop by using the break keyword, or you can step to the next
iteration by using the continue keyword. You also can exit any loop by using
a goto, return, or throw statement.

The first example in this topic shows the most typical kind of for loop, which makes the
following choices for the sections.

The initializer declares and initializes a local loop variable, i, that maintains a count of
the iterations of the loop.

The condition checks the value of the loop variable against a known final value, 5.

The iterator section uses a postfix increment statement, i++, to tally each iteration of the
loop.

The following example illustrates several less common choices: assigning a value to an external
loop variable in the initializer section, invoking the Console.WriteLine method in both the
initializer and the iterator sections, and changing the values of two variables in the iterator
section.
C#
static void Main()
{
int i;
int j = 10;
for (i = 0, Console.WriteLine("Start: {0}",i); i < j; i++, j--, Console.WriteLine("i={0},
j={1}", i, j))
{
// Body of the loop.
}
}
// Output:
// Start: 0
// i=1, j=9
// i=2, j=8
// i=3, j=7

// i=4, j=6
// i=5, j=5
All of the expressions that define a for statement are optional. For example, the following
statement creates an infinite loop.
C#
for (; ; )
{
// ...
}

foreach, in (C# Reference)


The foreach statement repeats a group of embedded statements for each element in an array or
an object collection that implements
the System.Collections.IEnumerable or System.Collections.Generic.IEnumerable<T>interface.
The foreach statement is used to iterate through the collection to get the information that you
want, but can not be used to add or remove items from the source collection to avoid
unpredictable side effects. If you need to add or remove items from the source collection, use
a for loop.
The embedded statements continue to execute for each element in the array or collection. After
the iteration has been completed for all the elements in the collection, control is transferred to the
next statement following the foreach block.
At any point within the foreach block, you can break out of the loop by using
the break keyword, or step to the next iteration in the loop by using the continue keyword.
A foreach loop can also be exited by the goto, return, or throw statements.
For more information about the foreach keyword and code samples, see the following topics:
Using foreach with Arrays (C# Programming Guide)
How to: Access a Collection Class with foreach (C# Programming Guide)
Example
The following code shows three examples:

a typical foreach loop that displays the contents of an array of integers

a for loop that does the same thing

a foreach loop that maintains a count of the number of elements in the array

C#
class ForEachTest
{
static void Main(string[] args)
{
int[] fibarray = new int[] { 0, 1, 1, 2, 3, 5, 8, 13 };
foreach (int element in fibarray)
{
System.Console.WriteLine(element);
}
System.Console.WriteLine();
// Compare the previous loop to a similar for loop.
for (int i = 0; i < fibarray.Length; i++)
{
System.Console.WriteLine(fibarray[i]);
}
System.Console.WriteLine();
// You can maintain a count of the elements in the collection.
int count = 0;
foreach (int element in fibarray)
{
count += 1;
System.Console.WriteLine("Element #{0}: {1}", count, element);
}
System.Console.WriteLine("Number of elements in the array: {0}", count);
}
// Output:
// 0
// 1
// 1
// 2
// 3
// 5
// 8
// 13
// 0
// 1
// 1
// 2

// 3
// 5
// 8
// 13
// Element #1: 0
// Element #2: 1
// Element #3: 1
// Element #4: 2
// Element #5: 3
// Element #6: 5
// Element #7: 8
// Element #8: 13
// Number of elements in the array: 8
}

while (C# Reference)


The while statement executes a statement or a block of statements until a specified expression
evaluates to false.
Example
C#
class WhileTest
{
static void Main()
{
int n = 1;
while (n < 6)
{
Console.WriteLine("Current value of n is {0}", n);
n++;
}
}
}
/*
Output:
Current value of n is 1
Current value of n is 2
Current value of n is 3
Current value of n is 4
Current value of n is 5
*/

C#
class WhileTest2
{
static void Main()
{
int n = 1;
while (n++ < 6)
{
Console.WriteLine("Current value of n is {0}", n);
}
}
}
/*
Output:
Current value of n is 2
Current value of n is 3
Current value of n is 4
Current value of n is 5
Current value of n is 6
*/
Because the test of the while expression takes place before each execution of the loop,
a while loop executes zero or more times. This differs from the do loop, which executes one or
more times.
A while loop can be terminated when a break, goto, return, or throw statement transfers control
outside the loop. To pass control to the next iteration without exiting the loop, use
the continue statement. Notice the difference in output in the three previous examples, depending
on where int n is incremented. In the example below no output is generated.
C#
class WhileTest3
{
static void Main()
{
int n = 5;
while (++n < 6)
{
Console.WriteLine("Current value of n is {0}", n);
}
}
}
Understand Error Handling

Exceptions and Exception Handling (C# Programming Guide)


The C# language's exception handling features help you deal with any unexpected or exceptional
situations that occur when a program is running. Exception handling uses the try, catch,
and finally keywords to try actions that may not succeed, to handle failures when you decide that
it is reasonable to do so, and to clean up resources afterward. Exceptions can be generated by the
common language runtime (CLR), by the .NET Framework or any third-party libraries, or by
application code. Exceptions are created by using the throw keyword.
In many cases, an exception may be thrown not by a method that your code has called directly,
but by another method further down in the call stack. When this happens, the CLR will unwind
the stack, looking for a method with a catch block for the specific exception type, and it will
execute the first such catch block that if finds. If it finds no appropriate catch block anywhere in
the call stack, it will terminate the process and display a message to the user.
In this example, a method tests for division by zero and catches the error. Without the exception
handling, this program would terminate with a DivideByZeroException was unhandled error.
C#
class ExceptionTest
{
static double SafeDivision(double x, double y)
{
if (y == 0)
throw new System.DivideByZeroException();
return x / y;
}
static void Main()
{
// Input for test purposes. Change the values to see
// exception handling behavior.
double a = 98, b = 0;
double result = 0;
try
{
result = SafeDivision(a, b);
Console.WriteLine("{0} divided by {1} = {2}", a, b, result);
}

catch (DivideByZeroException e)
{
Console.WriteLine("Attempted divide by zero.");
}
}
}
Exceptions Overview
Exceptions have the following properties:

Exceptions are types that all ultimately derive from System.Exception.

Use a try block around the statements that might throw exceptions.

Once an exception occurs in the try block, the flow of control jumps to the first
associated exception handler that is present anywhere in the call stack. In C#,
the catch keyword is used to define an exception handler.

If no exception handler for a given exception is present, the program stops executing with
an error message.

Do not catch an exception unless you can handle it and leave the application in a known
state. If you catch System.Exception, rethrow it using the throw keyword at the end of
the catch block.

If a catch block defines an exception variable, you can use it to obtain more information
about the type of exception that occurred.

Exceptions can be explicitly generated by a program by using the throw keyword.

Exception objects contain detailed information about the error, such as the state of the
call stack and a text description of the error.

Code in a finally block is executed even if an exception is thrown. Use a finally block to
release resources, for example to close any streams or files that were opened in
the try block.

Exception Handling (C# Programming Guide)


A try block is used by C# programmers to partition code that might be affected by an
exception. Associated catch blocks are used to handle any resulting exceptions.

A finally block contains code that is run regardless of whether or not an exception is thrown
in the try block, such as releasing resources that are allocated in the try block. A try block
requires one or more associated catch blocks, or a finally block, or both.
The following examples show a try-catch statement, a try-finally statement, and a trycatch-finally statement.

C#
try
{
// Code to try goes here.
}
catch (SomeSpecificException ex)
{
// Code to handle the exception goes here.
// Only catch exceptions that you know how to handle.
// Never catch base class System.Exception without
// rethrowing it at the end of the catch block.
}
C#
try
{
// Code to try goes here.
}
finally
{
// Code to execute after the try block goes here.
}
C#
try
{
// Code to try goes here.
}
catch (SomeSpecificException ex)
{
// Code to handle the exception goes here.
}
finally
{
// Code to execute after the try (and possibly catch) blocks
// goes here.
}
A try block without a catch or finally block causes a compiler error.

Catch Blocks
A catch block can specify the type of exception to catch. The type specification is called
an exception filter. The exception type should be derived from Exception. In general, do not
specify Exception as the exception filter unless either you know how to handle all exceptions
that might be thrown in the try block, or you have included a throwstatement at the end of
your catch block.
Multiple catch blocks with different exception filters can be chained together. The catch blocks
are evaluated from top to bottom in your code, but only one catch block is executed for each
exception that is thrown. The first catchblock that specifies the exact type or a base class of the
thrown exception is executed. If no catch block specifies a matching exception filter,
a catch block that does not have a filter is selected, if one is present in the statement. It is
important to position catch blocks with the most specific (that is, the most derived) exception
types first.
You should catch exceptions when the following conditions are true:

You have a good understanding of why the exception might be thrown, and you can
implement a specific recovery, such as prompting the user to enter a new file name when
you catch a FileNotFoundExceptionobject.

You can create and throw a new, more specific exception.


C#
int GetInt(int[] array, int index)
{
try
{
return array[index];
}
catch(System.IndexOutOfRangeException e)
{
throw new System.ArgumentOutOfRangeException(
"Parameter index is out of range.");
}
}

You want to partially handle an exception before passing it on for additional handling. In
the following example, a catch block is used to add an entry to an error log before rethrowing the exception.
C#
try
{
// Try to access a resource.
}
catch (System.UnauthorizedAccessException e)
{
// Call a custom error logging procedure.
LogError(e);
// Re-throw the error.
throw;
}

Finally Blocks
A finally block enables you to clean up actions that are performed in a try block. If present,
the finally block executes last, after the try block and any matched catch block. A finally block
always runs, regardless of whether an exception is thrown or a catch block matching the
exception type is found.
The finally block can be used to release resources such as file streams, database connections, and
graphics handles without waiting for the garbage collector in the runtime to finalize the objects.
See using Statement (C# Reference)for more information.
In the following example, the finally block is used to close a file that is opened in the try block.
Notice that the state of the file handle is checked before the file is closed. If the try block cannot
open the file, the file handle still has the value null and the finally block does not try to close it.
Alternatively, if the file is opened successfully in the try block, the finally block closes the open
file.
C#
System.IO.FileStream file = null;
System.IO.FileInfo fileinfo = new System.IO.FileInfo("C:\\file.txt");
try
{
file = fileinfo.OpenWrite();

file.WriteByte(0xF);
}
finally
{
// Check for null because OpenWrite might have failed.
if (file != null)
{
file.Close();
}
}
Creating and Throwing Exceptions (C# Programming Guide)
Exceptions are used to indicate that an error has occurred while running the program. Exception
objects that describe an error are created and then thrown with the throw keyword. The runtime
then searches for the most compatible exception handler.
Programmers should throw exceptions when one or more of the following conditions are true:

The method cannot complete its defined functionality.


For example, if a parameter to a method has an invalid value:
C#
static void CopyObject(SampleClass original)
{
if (original == null)
{
throw new System.ArgumentException("Parameter cannot be null", "original");
}
}

An inappropriate call to an object is made, based on the object state.


One example might be trying to write to a read-only file. In cases where an object state
does not allow an operation, throw an instance of InvalidOperationException or an object
based on a derivation of this class. This is an example of a method that throws
an InvalidOperationException object:
C#

class ProgramLog
{
System.IO.FileStream logFile = null;
void OpenLog(System.IO.FileInfo fileName, System.IO.FileMode mode) {}
void WriteLog()
{
if (!this.logFile.CanWrite)
{
throw new System.InvalidOperationException("Logfile cannot be read-only");
}
// Else write data to the log and return.
}
}

When an argument to a method causes an exception.


In this case, the original exception should be caught and an ArgumentException instance
should be created. The original exception should be passed to the constructor of
the ArgumentException as the InnerExceptionparameter:
C#
static int GetValueFromArray(int[] array, int index)
{
try
{
return array[index];
}
catch (System.IndexOutOfRangeException ex)
{
System.ArgumentException argEx = new System.ArgumentException("Index is
out of range", "index", ex);
throw argEx;
}
}

Exceptions contain a property named StackTrace. This string contains the name of the methods
on the current call stack, together with the file name and line number where the exception was
thrown for each method. A StackTrace object is created automatically by the common language

runtime (CLR) from the point of the throw statement, so that exceptions must be thrown from
the point where the stack trace should begin.
All exceptions contain a property named Message. This string should be set to explain the reason
for the exception. Note that information that is sensitive to security should not be put in the
message text. In addition to Message,ArgumentException contains a property
named ParamName that should be set to the name of the argument that caused the exception to
be thrown. In the case of a property setter, ParamName should be set to value.
Public and protected methods members should throw exceptions whenever they cannot complete
their intended functions. The exception class that is thrown should be the most specific exception
available that fits the error conditions. These exceptions should be documented as part of the
class functionality, and derived classes or updates to the original class should retain the same
behavior for backward compatibility.
Things to Avoid When Throwing Exceptions
The following list identifies practices to avoid when throwing exceptions:

Exceptions should not be used to change the flow of a program as part of ordinary
execution. Exceptions should only be used to report and handle error conditions.

Exceptions should not be returned as a return value or parameter instead of being thrown.

Do not
throw System.Exception, System.SystemException, System.NullReferenceException,
orSystem.IndexOutOfRangeException intentionally from your own source code.

Do not create exceptions that can be thrown in debug mode but not release mode. To
identify run-time errors during the development phase, use Debug Assert instead.

Defining Exception Classes


Programs can throw a predefined exception class in the System namespace (except where
previously noted), or create their own exception classes by deriving from Exception. The derived
classes should define at least four constructors: one default constructor, one that sets the message
property, and one that sets both the Message and InnerExceptionproperties. The fourth
constructor is used to serialize the exception. New exception classes should be serializable. For
example:
C#

[Serializable()]
public class InvalidDepartmentException : System.Exception
{
public InvalidDepartmentException() : base() { }
public InvalidDepartmentException(string message) : base(message) { }
public InvalidDepartmentException(string message, System.Exception inner) :
base(message, inner) { }
// A constructor is needed for serialization when an
// exception propagates from a remoting server to the client.
protected InvalidDepartmentException(System.Runtime.Serialization.SerializationInfo
info,
System.Runtime.Serialization.StreamingContext context) { }
}
New properties should only be added to the exception class when the data they provide is useful
to resolving the exception. If new properties are added to the derived exception
class, ToString() should be overridden to return the added information.
Compiler-Generated Exceptions (C# Programming Guide)
Some exceptions are thrown automatically by the .NET Framework's common language runtime
(CLR) when basic operations fail. These exceptions and their error conditions are listed in the
following table.

Exception

Description

ArithmeticException

A base class for exceptions that occur during arithmetic operations,


such as DivideByZeroException andOverflowException.

ArrayTypeMismatchException

Thrown when an array cannot store a given element because the actual
type of the element is incompatible with the actual type of the array.

DivideByZeroException

Thrown when an attempt is made to divide an integral value by zero.

IndexOutOfRangeException

Thrown when an attempt is made to index an array when the index is


less than zero or outside the bounds of the array.

InvalidCastException

Thrown when an explicit conversion from a base type to an interface or


to a derived type fails at runtime.

NullReferenceException

Thrown when you attempt to reference an object whose value is null.

OutOfMemoryException

Thrown when an attempt to allocate memory using thenew operator


fails. This indicates that the memory available to the common language
runtime has been exhausted.

OverflowException

Thrown when an arithmetic operation in a checkedcontext overflows.

StackOverflowException

Thrown when the execution stack is exhausted by having too many


pending method calls; usually indicates a very deep or infinite
recursion.

TypeInitializationException

Thrown when a static constructor throws an exception and no


compatible catch clause exists to catch it.

How to: Handle an Exception Using try/catch (C# Programming Guide)


The purpose of a try-catch block is to catch and handle an exception generated by working code.
Some exceptions can be handled in a catch block and the problem solved without the exception

being re-thrown; however, more often the only thing that you can do is make sure that the
appropriate exception is thrown.
Example
In this example, IndexOutOfRangeException is not the most appropriate
exception: ArgumentOutOfRangeExceptionmakes more sense for the method because the error
is caused by the index argument passed in by the caller.
C#
class TestTryCatch
{
static int GetInt(int[] array, int index)
{
try
{
return array[index];
}
catch (System.IndexOutOfRangeException e) // CS0168
{
System.Console.WriteLine(e.Message);
// Set IndexOutOfRangeException to the new exception's InnerException.
throw new System.ArgumentOutOfRangeException("index parameter is out of
range.", e);
}
}
}
How to: Execute Cleanup Code Using finally (C# Programming Guide)
The purpose of a finally statement is to ensure that the necessary cleanup of objects, usually
objects that are holding external resources, occurs immediately, even if an exception is thrown.
One example of such cleanup is calling Close on aFileStream immediately after use instead of
waiting for the object to be garbage collected by the common language runtime, as follows:
C#
static void CodeWithoutCleanup()
{
System.IO.FileStream file = null;
System.IO.FileInfo fileInfo = new System.IO.FileInfo("C:\\file.txt");

file = fileInfo.OpenWrite();
file.WriteByte(0xF);
file.Close();
}
Example
To turn the previous code into a try-catch-finally statement, the cleanup code is separated from
the working code, as follows.
C#
static void CodeWithCleanup()
{
System.IO.FileStream file = null;
System.IO.FileInfo fileInfo = null;
try
{
fileInfo = new System.IO.FileInfo("C:\\file.txt");
file = fileInfo.OpenWrite();
file.WriteByte(0xF);
}
catch(System.UnauthorizedAccessException e)
{
System.Console.WriteLine(e.Message);
}
finally
{
if (file != null)
{
file.Close();
}
}
}
Because an exception can occur at any time within the try block before the OpenWrite() call, or
the OpenWrite()call itself could fail, we are not guaranteed that the file is open when we try to
close it. The finally block adds a check to make sure that the FileStream object is not null before

you call the Close method. Without the null check, thefinally block could throw its
own NullReferenceException, but throwing exceptions in finally blocks should be avoided if it is
possible.
A database connection is another good candidate for being closed in a finally block. Because the
number of connections allowed to a database server is sometimes limited, you should close
database connections as quickly as possible. If an exception is thrown before you can close your
connection, this is another case where using the finallyblock is better than waiting for garbage
collection.
How to: Catch a non-CLS Exception
Some .NET languages, including C++/CLI, allow objects to throw exceptions that do not derive
from Exception. Such exceptions are called non-CLS exceptions or non-Exceptions. In Visual C#
you cannot throw non-CLS exceptions, but you can catch them in two ways:

Within a catch (Exception e) block as a RuntimeWrappedException.


By default, a Visual C# assembly catches non-CLS exceptions as wrapped exceptions.
Use this method if you need access to the original exception, which can be accessed
through the WrappedException property. The procedure later in this topic explains how to
catch exceptions in this manner.

Within a general catch block (a catch block without an exception type specified) that is
put after a catch (Exception) or catch (Exception e) block.
Use this method when you want to perform some action (such as writing to a log file) in
response to non-CLS exceptions, and you do not need access to the exception
information. By default the common language runtime wraps all exceptions. To disable
this behavior, add this assembly-level attribute to your code, typically in the
AssemblyInfo.cs file: [assembly:
RuntimeCompatibilityAttribute(WrapNonExceptionThrows = false)].

To catch a non-CLS exception


1. Within a catch(Exception e) block, use the as keyword to test whether e can be cast to
aRuntimeWrappedException.
2. Access the original exception through the WrappedException property.
Example

The following example shows how to catch a non-CLS exception that was thrown from a class
library written in C++/CLR. Note that in this example, the Visual C# client code knows in
advance that the exception type being thrown is a System.String. You can cast
the WrappedException property back its original type as long as that type is accessible from your
code.
// Class library written in C++/CLR.
ThrowNonCLS.Class1 myClass = new ThrowNonCLS.Class1();
try
{
// throws gcnew System::String(
// "I do not derive from System.Exception!");
myClass.TestThrow();
}

catch (Exception e)
{
RuntimeWrappedException rwe = e as RuntimeWrappedException;
if (rwe != null)
{
String s = rwe.WrappedException as String;
if (s != null)
{
Console.WriteLine(s);
}
}
else
{
// Handle other System.Exception types.
}
}

Вам также может понравиться