Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Visual CSharp .NET Programming (2002) [eng]

.pdf
Скачиваний:
58
Добавлен:
16.08.2013
Размер:
7.98 Mб
Скачать

include a statement block-as many statements as you'd like-even when this wasn't strictly necessary because I only had one statement to execute.

Using goto in a switch

Of course, you can easily simplify this code by using a switch statement rather than goto statements and labels:

string msg = "";

switch (txtBeatle.Text) { case "John":

msg = "I like John best, too!"; break;

case "Ringo":

msg = "Are you a drummer?"; break;

}

if (msg != "")

MessageBox.Show(msg,"Beatle choice", MessageBoxButtons.OK);

Let's say you want to add another possibility to this switch statement, for people who enter 'Mick' but really mean they like John. For this situation, a goto case statement-to the "John" case-can be added at the end of the "Mick" case. Users who enter "Mick" will be told they really like John and then redirected so they also get the "John" message:

switch (txtBeatle.Text) { case "John":

msg = "I like John best, too!"; break;

case "Mick":

MessageBox.Show("People who like Mick really like John.", "Beatle choice", MessageBoxButtons.OK);

goto case "John"; case "Ringo":

msg = "Are you a drummer?"; break;

}

while Statements

Looping is an important flow control element of most programs. I've shown you examples of looping using for and foreach, but I haven't yet demonstrated while and do…while statements-so let's take a quick look.

As in most other languages that have a while statement, the code within a while statement block is executed as long as the Boolean test at the beginning of the statement evaluates to

true. As soon as the test evaluates to false, execution jumps to the statement immediately following the while statement block.

For example, the following while loop displays the integers from 1 to 9 in the title bar of a Form:

int i = 1;

string caption = "While"; while (i < 10){

caption = caption + " + " + i.ToString(); i++;

}

this.Text = caption;

do…while Statements

The do…while statement works like the while statement, except that the evaluation takes place at the end of the loop. Statements in a do…while loop will get executed at least onceeven if the condition evaluates to false at the end. In contrast, if the condition is false, the statements in a while loop never get executed at all because the condition is evaluated at the start. Most of the time, the same results can be accomplished using either loop syntax, but there are some times when one works better than the other.

Note For example, if you are reading and operating on data, and don't know whether there is any data, you might want use a while statement to test for the existence of the dataperhaps testing for an end of file marker-so that if the data doesn't exist, no statements get executed. For more on reading files, see Chapter 10, "Working with Streams and Files."

Here's a do…while statement that displays the first 9 numbers in the Fibonacci series in the title bar of a form:

int A = 1; int B = 1; int C; string caption = "Fibonacci: "; do {

caption = caption + A.ToString() + " "; C = A + B;

A = B;

B = C;

} while (A < 50); this.Text = caption;

Note If the condition in this loop were impossible, e.g., A < 0, it would still print the first number in the series, which would not be the case in a while loop with the condition at the top.

Structs

A struct is a simple user-defined type, a poor person's lightweight alternative to a class.

Like classes, structs can contain properties, methods, and fields. Unlike classes, structs do not support inheritance. More precisely, structs derive from System.Object, like all types in C#,

but they cannot inherit from any other class (or struct), and no class or struct can derive from a struct.

Note A class can also be marked so that it cannot be inherited from, by using the sealed keyword, as I'll explain in Chapter 8.

Structs are value types, not reference types, and-like simple value types-they can be used without the new keyword.

Memory, Value-Type Variables, and Reference-Type Variables

Value-type variables hold their values, or data, within their own memory allocation. Technically, these values are stored in memory allocated for the value-type variable within a structure known as the program's stack, and can be accessed from within the program as long as they are within scope.

In contrast, reference-type variables-such as classes and strings-are implemented using a global memory structure called the run-time heap. Reference-type variables contain instances of classes, which are 'pointers' to objects stored in memory on the heap.

Here's a simple example of a struct that might be used to store information about an employee:

public struct Employee {

public string fullName, rank; public long SSN;

public Employee (string fullName, string rank, long SSN){ this.fullName = fullName;

this.rank = rank; this.SSN = SSN;

}

}

Note You can create properties, using get and set accessors, within structs. Here's one way the Employee struct might be declared, initialized, and used:

//Declare an instance Employee HopperK;

//Initialize

HopperK.fullName = "Ken Hopper";

HopperK.rank = "Master Sergeant";

HopperK.SSN = 000112222;

// Display it

string str = HopperK.fullName + " has the rank of " + HopperK.rank + "!"; MessageBox.Show(str, "Structs Forever!",MessageBoxButtons.OK,

MessageBoxIcon.Information);

Alternatively, you could create the instance of the struct by invoking its constructor, and initialize it in one fell swoop:

Employee DavisJ = new Employee("Julian Davis", "City Hall", 123456789);

Exceptions

An exception is an unexpected condition in a program. For example, an exception may occur if you attempt to connect to the database but can't, because it's down. The C# Exception object is used to store information about errors and abnormal events.

In real-world practice, industrial-strength programs must anticipate run-time errors. For example, a network resource might not be available because the network is down. Or a file can't be written to disk because the disk drive is full. With the best code in the world, these things happen. In the release version of a program, these and other errors must be handled, which in C# is done with exceptions.

What does it mean to 'handle' an exception (or error)? These are the basics:

Your program should not crash under any circumstances.

If the situation can be recovered from, and the program can continue execution, it should.

If execution cannot continue, a reasonably comprehensible message should explain that the program is terminating, and if appropriate, explain why.

Data should not be lost due to an unplanned failure.

The program should shut down gently.

In any case, information necessary for debugging the problem should be saved, either to a file or the system event log. (It's easy to simultaneously send the user a message and write to the system log with MessageBox.Show, as I demonstrated in Chapter 3.)

Understanding Structured Exception Handling

Structured exception handling is recognized as the best way for a programming language to deal with common errors.

Using try…catch…finally Statements

When an exception does occur, you can use try…catch…finally statements to handle it.

The program statements in the try block are the body of the program that is being monitored for errors. Statements in a catch block are executed in response to a particular exception being thrown, as indicated in the catch statement's argument-or, if without an argument, the statement will catch any kind of exception, in which case it is referred to as a general catch clause.

As you would expect, code in the optional finally block is executed, whether an error has been caught or not.

Note Not only is finally optional, so is catch. In other words, you can have a try without a finally and also a try…finally construction with no catch block, as I'll show you in a moment.

For example, if you have a form with a TextBox control and a Button control, you could add the following code in the button's Click event to catch different kinds of problems:

private void btnExceptions_Click(object sender, System.EventArgs e) { int i; double d;

try {

i = Convert.ToInt16(txtIn.Text); d = 42 / i;

}

catch (DivideByZeroException){

MessageBox.Show("You are naughty to even think of dividing by zero!", "Exceptions", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

catch (FormatException){

MessageBox.Show("Please enter a number!", "Exceptions", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

}

In the example, catch deals with the situation if the user enters zero-which would cause a division by zero-as shown in Figure 6.7.

Figure 6.7: The DivideByZero-Exception (left) and FormatException (right) are caught.

As you can also see in the figure, a FormatException catch block can be used to handle the exception that occurs when the user enters something that can't be converted to integer by Convert.ToInt16.

It's possible to have the first catch filter without any conditions:

catch

statement block

You could also just catch the general Exception type:

catch (Exception excep)

In these cases, the catch filter will catch all errors. In some situations, you can use this as a centralized error-processing mechanism along the lines of "if any error happens, go here and take care of it." Often, however, it is a better idea to use specific catch clauses to handle certain types of exceptions. A final, generic catch clause could deal with all exceptions that don't need special handling-or that you didn't think of when you wrote the code.

Listing 6.1 shows the exception example with an added, general catch clause at the end.

Listing 6.1: Handling Exceptions with try…catch

private void btnExceptions_Click(object sender, System.EventArgs e) { int i; double d;

try {

i = Convert.ToInt16 (txtIn.Text); d = 42 / i;

}

catch (DivideByZeroException){

MessageBox.Show("You are naughty to even think of dividing by zero!", "Exceptions", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

catch (FormatException){

MessageBox.Show("Please enter a number!", "Exceptions", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

catch (Exception excep) { MessageBox.Show(excep.Message);

}

}

Warning If you reverse the order shown in Listing 6.1, and put the general catch clause before the specific catch exceptions, then the specific exception blocks are never processed.

If you run the code shown in Listing 6.1, you can trigger an error that is not handled by one of the specific catch handlers-for example, an overflow exception if the user enters a number larger than will fit in the data type, as shown in Figure 6.8.

Figure 6.8: Since the OverflowException is not handled by a specific catch clause, it is handled by the general catch clause at the end of the catch blocks.

By the way, instead of displaying the Message property of the Exception object, you could make use of its ToString method, which contains a great deal of information, including the procedure, method, and line number that threw the exception. If you changed the final catch clause in Listing 6.1 to show the information returned by the ToString method:

catch (Exception excep) { MessageBox.Show(excep.ToString());

}

it would appear as shown in Figure 6.9.

Figure 6.9: The ToString method of the Exception object contains a great deal of information about a thrown exception.

Boy, is that finally clause out of its mind, or what? You can't stop it from being executed, regardless of whether exceptions are thrown or, if thrown, handled or unhandled. This makes the finally clause the place to put clean-up code that you want executed no matter what happens.

Listing 6.2 shows the exception handling demonstration with a finally clause added. When you run the program, you'll see that the message box invoked in the finally clause is always displayed whether or not the exceptions are handled.

Listing 6.2: Exception Handling with a finally Block Added

private void btnExceptions_Click(object sender, System.EventArgs e) { int i; double d;

try {

i = Convert.ToInt16(txtIn.Text); d = 42 / i;

}

catch (DivideByZeroException){

MessageBox.Show("You are naughty to even think of dividing by zero!", "Exceptions", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

catch (FormatException){

MessageBox.Show("Please enter an integer!", "Exceptions", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

catch (Exception excep) { MessageBox.Show(excep.Message); MessageBox.Show(excep.ToString());

}

finally {

MessageBox.Show("You can't stop me from being executed!", "Exceptions",

MessageBoxButtons.OK, MessageBoxIcon.Information);

}

}

Note In the real world, you'll want to use finally blocks to close open files and database connections, and generally to make sure that all resources used by a program are released.

What happens if you leave off the catch blocks-resulting in a try…finally statement? We can find out by modifying the code shown in Listing 6.2 to remove the catch blocks:

private void btnExceptions_Click(object sender, System.EventArgs e) { int i; double d;

try {

i = Convert.ToInt16(txtIn.Text); d = 42 / i;

}

finally {

MessageBox.Show("You can't stop me from being executed!", "Exceptions",

MessageBoxButtons.OK, MessageBoxIcon.Information);

}

}

If the click event procedure runs without generating exceptions, the code in the finally block executes as you'd expect and the message box displays.

What happens next depends on whether you are running in debug mode in the development environment or whether the compiled executable was launched. Within the development environment, a Visual Studio exception message is displayed.

If Break is clicked, the program terminates. If Continue is clicked, the code in the finally clause is executed and the message box displayed, and then the application shuts down.

Of somewhat more interest is the behavior of the stand-alone executable (because exception handling is meant to deal with problems that occur in the run-time environment, not at design time).

If you run the stand-alone executable, the finally block code executes. Then, if an exception had been thrown, the .NET Framework dialog shown in Figure 6.10 is displayed.

Figure 6.10: When run outside the development environment, if an exception is thrown, the finally block executes, and then this dialog is displayed.

Not bad, really, as a user interface for all unhandled exceptions-and the finally code did execute before it was displayed, so in the real world you could have saved data, closed open files, and so forth!

If the user clicks Continue in the dialog shown in Figure 6.10, the application reopens.

Throwing Exceptions

Throwing an exception means creating your own exception under certain conditions. Throwing custom exceptions should not be done to facilitate normal program communication, but it may make sense as part of a scheme for dealing with logical errors, which occur when a program works properly but produces wrong results. Throwing an exception is what your application should do in many circumstances if it asked to do something impossible.

To throw an exception, use the throw keyword with a new instantiation of the Exception class, or one of the classes derived from Exception (the Exception object and classes are described in the next section). ApplicationException is the subclass of Exception used for exceptions thrown by an application, so it's a good one to throw.

Note As a good programming practice, your application should throw only ApplicationException objects, or those that inherit ApplicationException. It's not good practice to throw instances of System.Exception.

In the real world, you may want to subclass ApplicationException to add functionality to the exception thrown.

A string representing the Exception.Message property is used to instantiate the new exception, which can then be used to catch it, as in this example:

throw(new ApplicationException("AttilaTheHun"));

This exception would probably be caught within a catch (Exception excep) block using a conditional:

catch (Exception excep) {

if (excep.Message == "AttilaTheHun"){

...

Let's look at an example of throwing and catching an exception in practice.

There is something in business-and software development-known as the 'cockroach' theory: if one problem becomes apparent, there are probably more problems lurking (like roaches).

Suppose, in the example I've used so far to show you exceptions, that the user types

the phrase 'cockroaches' in the TextBox. We take it this means there are more roaches in the system and decide to throw an exception. Here's how to throw a 'roaches' exception:

if (txtIn.Text == "cockroaches"){

throw(new ApplicationException("roaches"));

}

The next step is to catch the exception to handle the situation and make sure the roaches check in but don't check out:

catch (Exception excep) {

if (excep.Message == "roaches"){

MessageBox.Show("You have roaches in your program!", "Exceptions", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

else {

...