Академический Документы
Профессиональный Документы
Культура Документы
Understanding the elements of computer storage will enable a GIS user to design optimum
storage for different types of data.
2.1 Bits
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).
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)
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
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.
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.
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
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.
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.
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.
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 (").
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/
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.
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.
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.
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.
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.
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.
Data Types
Operators
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
, 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.
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
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
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
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
/>.
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
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
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
//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:
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.
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
await expression
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
await expression
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 (; ; )
{
// ...
}
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
}
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
catch (DivideByZeroException e)
{
Console.WriteLine("Attempted divide by zero.");
}
}
}
Exceptions Overview
Exceptions have the following properties:
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.
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.
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 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:
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.
}
}
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.
[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
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
IndexOutOfRangeException
InvalidCastException
NullReferenceException
OutOfMemoryException
OverflowException
StackOverflowException
TypeInitializationException
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 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)].
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.
}
}