Let us consider a simple example of exception handling. The application in Fig. 12.2 uses exception handling to process any DivideByZeroExceptions and FormatExceptions that might arise. The application displays two TextBoxes in which the user can type integers. When the user presses Click To Divide, the program invokes event handler DivideButton_Click (lines 1748), which obtains the user's input, converts the input values to type int and divides the first number (numerator) by the second number (denominator). Assuming that the user provides integers as input and does not specify 0 as the denominator for the division, DivideButton_Click displays the division result in OutputLabel. However, if the user inputs a noninteger value or supplies 0 as the denominator, an exception occurs. This program demonstrates how to catch and handle (i.e., deal with) such exceptionsin this case, displaying an error message and allowing the user to enter another set of values.
Figure 12.2. Exception handlers for FormatException and DivideByZeroException.
(This item is displayed on pages 568 - 569 in the print version)
1 // Fig. 12.2: DivideByZeroTest.cs 2 // Exception handlers for FormatException and DivideByZeroException. 3 using System; 4 using System.Windows.Forms; 5 6 namespace DivideByZeroTest 7 { 8 public partial class DivideByZeroTestForm : Form 9 { 10 public DivideByZeroTestForm() 11 { 12 InitializeComponent(); 13 } // end constructor 14 15 // obtain 2 integers from the user 16 // and divide numerator by denominator 17 private void DivideButton_Click( object sender, EventArgs e ) 18 { 19 OutputLabel.Text = ""; // clear Label OutputLabel 20 21 // retrieve user input and calculate quotient 22 try 23 { 24 // Convert.ToInt32 generates FormatException 25 // if argument is not an integer 26 int numerator = Convert.ToInt32( NumeratorTextBox.Text ); 27 int denominator = Convert.ToInt32( DenominatorTextBox.Text ); 28 29 // division generates DivideByZeroException 30 // if denominator is 0 31 int result = numerator / denominator; 32 33 // display result in OutputLabel 34 OutputLabel.Text = result.ToString(); 35 } // end try 36 catch ( FormatException ) 37 { 38 MessageBox.Show( "You must enter two integers.", 39 "Invalid Number Format", MessageBoxButtons.OK, 40 MessageBoxIcon.Error ); 41 } // end catch 42 catch ( DivideByZeroException divideByZeroExceptionParameter ) 43 { 44 MessageBox.Show( divideByZeroExceptionParameter.Message, 45 "Attempted to Divide by Zero", MessageBoxButtons.OK, 46 MessageBoxIcon.Error ); 47 } // end catch 48 } // end method DivideButton_Click 49 } // end class DivideByZeroTestForm 50 } // end namespace DivideByZeroTest (a) (b) (c) (d) (e) |
Before we discuss the details of the program, let's consider the sample output windows in Fig. 12.2. The window in Fig. 12.2(a) shows a successful calculation, in which the user enters the numerator 100 and the denominator 7. Note that the result (14) is an int, because integer division always yields an int result. The next two windows, Fig. 12.2(b) and Fig. 12.2(c), demonstrate the result of an attempt to divide by zero. In integer arithmetic, the CLR tests for division by zero and generates a DivideByZeroException if the denominator is zero. The program detects the exception and displays the error message dialog in Fig. 12.2(c) indicating the attempt to divide by zero. The last two output windows, Fig. 12.2(d) and Fig. 12.2(e), depict the result of inputting a non-int valuein this case, the user enters "hello" in the second TextBox, as shown in Fig. 12.2(d). When the user clicks Click To Divide, the program attempts to convert the input strings into int values using method Convert.ToInt32 (lines 2627). If an argument passed to Convert.ToInt32 cannot be converted to an int value, the method throws a FormatException. The program catches the exception and displays the error message dialog in Fig. 12.2(e) indicating that the user must enter two ints. Notice that we did not include a parameter name for the catch at line 36. In the catch's block, we do not use any information from the FormatException object. Omitting the parameter name prevents the compiler from issuing a warning which indicates that we declared a variable, but did not use it in the catch block.
12.4.1. Enclosing Code in a TRy Block
Now we consider the user interactions and flow of control that yield the results shown in the sample output windows. The user inputs values into the TextBoxes that represent the numerator and denominator, then presses Click To Divide. At this point, the program invokes method DivideButton_Click. Line 19 assigns the empty string to OutputLabel to clear any prior result in preparation for a new calculation. Lines 2235 define a try block enclosing the code that might throw exceptions, as well as the code that is skipped when an exception occurs. For example, the program should not display a new result in OutputLabel (line 34) unless the calculation in line 31 completes successfully.
The two statements that read the ints from the TextBoxes (lines 2627) call method Convert.ToInt32 to convert strings to int values. This method throws a FormatException if it cannot convert its string argument to an int. If lines 2627 convert the values properly (i.e., no exceptions occur), then line 31 divides the numerator by the denominator and assigns the result to variable result. If denominator is 0, line 31 causes the CLR to throw a DivideByZeroException. If line 31 does not cause an exception to be thrown, then line 34 displays the result of the division.
12.4.2. Catching Exceptions
Exception-handling code appears in a catch block. In general, when an exception occurs in a TRy block, a corresponding catch block catches the exception and handles it. The try block in this example is followed by two catch blocksone that handles a FormatException (lines 3641) and one that handles a DivideByZeroException (lines 4247). A catch block specifies an exception parameter representing the exception that the catch block can handle. The catch block can use the parameter's identifier (which is chosen by the programmer) to interact with a caught exception object. If there is no need to use the exception object in the catch block, the exception parameter's identifier can be omitted. The type of the catch's parameter is the type of the exception that the catch block handles. Optionally, programmers can include a catch block that does not specify an exception type or an identifiersuch a catch block (known as a general catch clause) catches all exception types. At least one catch block and/or a finally block (discussed in Section 12.6) must immediately follow a TRy block.
In Fig. 12.2, the first catch block catches FormatExceptions (thrown by method Convert.ToInt32), and the second catch block catches DivideByZeroExceptions (thrown by the CLR). If an exception occurs, the program executes only the first matching catch block. Both exception handlers in this example display an error message dialog. After either catch block terminates, program control continues with the first statement after the last catch block (the end of the method, in this example). We will soon take a deeper look at how this flow of control works in exception handling.
12.4.3. Uncaught Exceptions
An uncaught exception is an exception for which there is no matching catch block. You saw the results of uncaught exceptions in the second and third outputs of Fig. 12.1. Recall that when exceptions occur in that example, the application terminates early (after displaying the exception's stack trace). The result of an uncaught exception depends on how you execute the programFig. 12.1 demonstrated the results of an uncaught exception when an application is executed in a Command Prompt. If you run the application from Visual Studio with debugging and the runtime environment detects an uncaught exception, the application pauses, and a window called the Exception Assistant appears indicating where the exception occurred, the type of the exception and links to helpful information on handling the exception. Figure 12.3 shows the Exception Assistant that is displayed if the user attempts to divide by zero in the application of Fig. 12.1.
Figure 12.3. Exception Assistant.
12.4.4. Termination Model of Exception Handling
When a method called in a program or the CLR detects a problem, the method or the CLR throws an exception. Recall that the point in the program at which an exception occurs is called the throw pointthis is an important location for debugging purposes (as we demonstrate in Section 12.7). If an exception occurs in a try block (such as a FormatException being thrown as a result of the code in line 27 in Fig. 12.2), the TRy block terminates immediately, and program control transfers to the first of the following catch blocks in which the exception parameter's type matches the type of the thrown exception. In Fig. 12.2, the first catch block catches FormatExceptions (which occur if input of an invalid type is entered); the second catch block catches DivideByZeroExceptions (which occur if an attempt is made to divide by zero). After the exception is handled, program control does not return to the throw point because the try block has expired (which also causes any of its local variables to go out of scope). Rather, control resumes after the last catch block. This is known as the termination model of exception handling. [Note: Some languages use the resumption model of exception handling, in which after an exception is handled, control resumes just after the throw point.]
|
If no exceptions occur in the TRy block, the program of Fig. 12.2 successfully completes the TRy block by ignoring the catch blocks in lines 3641 and 4247, and passing line 47. Then the program executes the first statement following the try and catch blocks. In this example, the program reaches the end of event handler DivideButton_Click (line 48), so the method terminates, and the program awaits the next user interaction.
The TRy block and its corresponding catch and finally blocks together form a try statement. It is important not to confuse the terms "try block" and "try statement"the term "TRy block" refers to the block of code following the keyword try (but before any catch or finally blocks), while the term "TRy statement" includes all the code from the opening TRy keyword to the end of the last catch or finally block. This includes the TRy block, as well as any associated catch blocks and finally block.
As with any other block of code, when a TRy block terminates, local variables defined in the block go out of scope. If a try block terminates due to an exception, the CLR searches for the first catch block that can process the type of exception that occurred. The CLR locates the matching catch by comparing the type of the thrown exception to each catch's parameter type. A match occurs if the types are identical or if the thrown exception's type is a derived class of the catch's parameter type. Once an exception is matched to a catch block, the code in that block executes and the other catch blocks in the TRy statement are ignored.
12.4.5. Flow of Control When Exceptions Occur
In the sample output of Fig. 12.2(b), the user inputs hello as the denominator. When line 27 executes, Convert.ToInt32 cannot convert this string to an int, so Convert.ToInt32 throws a FormatException object to indicate that the method was unable to convert the string to an int. When the exception occurs, the try block expires (terminates). Next, the CLR attempts to locate a matching catch block. A match occurs with the catch block in line 36, so the exception handler executes and the program ignores all other exception handlers following the TRy block.
|
In the sample output of Fig. 12.2(d), the user inputs 0 as the denominator. When the division in line 31 executes, a DivideByZeroException occurs. Once again, the try block terminates, and the program attempts to locate a matching catch block. In this case, the first catch block does not matchthe exception type in the catch-handler declaration is not the same as the type of the thrown exception, and FormatException is not a base class of DivideByZeroException. Therefore the program continues to search for a matching catch block, which it finds in line 42. Line 44 displays the value of property Message of class Exception, which contains the error message. Note that our program never "sets" this error message attribute. This is done by the CLR when it creates the exception object.
Preface
Index
Introduction to Computers, the Internet and Visual C#
Introduction to the Visual C# 2005 Express Edition IDE
Introduction to C# Applications
Introduction to Classes and Objects
Control Statements: Part 1
Control Statements: Part 2
Methods: A Deeper Look
Arrays
Classes and Objects: A Deeper Look
Object-Oriented Programming: Inheritance
Polymorphism, Interfaces & Operator Overloading
Exception Handling
Graphical User Interface Concepts: Part 1
Graphical User Interface Concepts: Part 2
Multithreading
Strings, Characters and Regular Expressions
Graphics and Multimedia
Files and Streams
Extensible Markup Language (XML)
Database, SQL and ADO.NET
ASP.NET 2.0, Web Forms and Web Controls
Web Services
Networking: Streams-Based Sockets and Datagrams
Searching and Sorting
Data Structures
Generics
Collections
Appendix A. Operator Precedence Chart
Appendix B. Number Systems
Appendix C. Using the Visual Studio 2005 Debugger
Appendix D. ASCII Character Set
Appendix E. Unicode®
Appendix F. Introduction to XHTML: Part 1
Appendix G. Introduction to XHTML: Part 2
Appendix H. HTML/XHTML Special Characters
Appendix I. HTML/XHTML Colors
Appendix J. ATM Case Study Code
Appendix K. UML 2: Additional Diagram Types
Appendix L. Simple Types
Index