- •Contents at a Glance
- •Table of Contents
- •Acknowledgments
- •Introduction
- •Who This Book Is For
- •Finding Your Best Starting Point in This Book
- •System Requirements
- •Conventions and Features in This Book
- •Conventions
- •Other Features
- •Code Samples
- •Installing the Code Samples
- •Using the Code Samples
- •Support for This Book
- •Questions and Comments
- •Beginning Programming with the Visual Studio 2008 Environment
- •Writing Your First Program
- •Using Namespaces
- •Creating a Graphical Application
- •Chapter 1 Quick Reference
- •Understanding Statements
- •Identifying Keywords
- •Using Variables
- •Naming Variables
- •Declaring Variables
- •Working with Primitive Data Types
- •Displaying Primitive Data Type Values
- •Using Arithmetic Operators
- •Operators and Types
- •Examining Arithmetic Operators
- •Controlling Precedence
- •Using Associativity to Evaluate Expressions
- •Associativity and the Assignment Operator
- •Incrementing and Decrementing Variables
- •Declaring Implicitly Typed Local Variables
- •Chapter 2 Quick Reference
- •Declaring Methods
- •Specifying the Method Declaration Syntax
- •Writing return Statements
- •Calling Methods
- •Specifying the Method Call Syntax
- •Applying Scope
- •Overloading Methods
- •Writing Methods
- •Chapter 3 Quick Reference
- •Declaring Boolean Variables
- •Using Boolean Operators
- •Understanding Equality and Relational Operators
- •Understanding Conditional Logical Operators
- •Summarizing Operator Precedence and Associativity
- •Using if Statements to Make Decisions
- •Understanding if Statement Syntax
- •Using Blocks to Group Statements
- •Cascading if Statements
- •Using switch Statements
- •Understanding switch Statement Syntax
- •Following the switch Statement Rules
- •Chapter 4 Quick Reference
- •Using Compound Assignment Operators
- •Writing while Statements
- •Writing for Statements
- •Understanding for Statement Scope
- •Writing do Statements
- •Chapter 5 Quick Reference
- •Coping with Errors
- •Trying Code and Catching Exceptions
- •Handling an Exception
- •Using Multiple catch Handlers
- •Catching Multiple Exceptions
- •Using Checked and Unchecked Integer Arithmetic
- •Writing Checked Statements
- •Writing Checked Expressions
- •Throwing Exceptions
- •Chapter 6 Quick Reference
- •The Purpose of Encapsulation
- •Controlling Accessibility
- •Working with Constructors
- •Overloading Constructors
- •Understanding static Methods and Data
- •Creating a Shared Field
- •Creating a static Field by Using the const Keyword
- •Chapter 7 Quick Reference
- •Copying Value Type Variables and Classes
- •Understanding Null Values and Nullable Types
- •Using Nullable Types
- •Understanding the Properties of Nullable Types
- •Using ref and out Parameters
- •Creating ref Parameters
- •Creating out Parameters
- •How Computer Memory Is Organized
- •Using the Stack and the Heap
- •The System.Object Class
- •Boxing
- •Unboxing
- •Casting Data Safely
- •The is Operator
- •The as Operator
- •Chapter 8 Quick Reference
- •Declaring an Enumeration
- •Using an Enumeration
- •Choosing Enumeration Literal Values
- •Choosing an Enumeration’s Underlying Type
- •Working with Enumerations
- •Working with Structures
- •Declaring a Structure
- •Understanding Structure and Class Differences
- •Declaring Structure Variables
- •Understanding Structure Initialization
- •Copying Structure Variables
- •Chapter 9 Quick Reference
- •What Is an Array?
- •Declaring Array Variables
- •Creating an Array Instance
- •Initializing Array Variables
- •Creating an Implicitly Typed Array
- •Accessing an Individual Array Element
- •Iterating Through an Array
- •Copying Arrays
- •What Are Collection Classes?
- •The ArrayList Collection Class
- •The Queue Collection Class
- •The Stack Collection Class
- •The Hashtable Collection Class
- •The SortedList Collection Class
- •Using Collection Initializers
- •Comparing Arrays and Collections
- •Using Collection Classes to Play Cards
- •Chapter 10 Quick Reference
- •Using Array Arguments
- •Declaring a params Array
- •Using params object[ ]
- •Using a params Array
- •Chapter 11 Quick Reference
- •What Is Inheritance?
- •Using Inheritance
- •Base Classes and Derived Classes
- •Calling Base Class Constructors
- •Assigning Classes
- •Declaring new Methods
- •Declaring Virtual Methods
- •Declaring override Methods
- •Understanding protected Access
- •Understanding Extension Methods
- •Chapter 12 Quick Reference
- •Understanding Interfaces
- •Interface Syntax
- •Interface Restrictions
- •Implementing an Interface
- •Referencing a Class Through Its Interface
- •Working with Multiple Interfaces
- •Abstract Classes
- •Abstract Methods
- •Sealed Classes
- •Sealed Methods
- •Implementing an Extensible Framework
- •Summarizing Keyword Combinations
- •Chapter 13 Quick Reference
- •The Life and Times of an Object
- •Writing Destructors
- •Why Use the Garbage Collector?
- •How Does the Garbage Collector Work?
- •Recommendations
- •Resource Management
- •Disposal Methods
- •Exception-Safe Disposal
- •The using Statement
- •Calling the Dispose Method from a Destructor
- •Making Code Exception-Safe
- •Chapter 14 Quick Reference
- •Implementing Encapsulation by Using Methods
- •What Are Properties?
- •Using Properties
- •Read-Only Properties
- •Write-Only Properties
- •Property Accessibility
- •Understanding the Property Restrictions
- •Declaring Interface Properties
- •Using Properties in a Windows Application
- •Generating Automatic Properties
- •Initializing Objects by Using Properties
- •Chapter 15 Quick Reference
- •What Is an Indexer?
- •An Example That Doesn’t Use Indexers
- •The Same Example Using Indexers
- •Understanding Indexer Accessors
- •Comparing Indexers and Arrays
- •Indexers in Interfaces
- •Using Indexers in a Windows Application
- •Chapter 16 Quick Reference
- •Declaring and Using Delegates
- •The Automated Factory Scenario
- •Implementing the Factory Without Using Delegates
- •Implementing the Factory by Using a Delegate
- •Using Delegates
- •Lambda Expressions and Delegates
- •Creating a Method Adapter
- •Using a Lambda Expression as an Adapter
- •The Form of Lambda Expressions
- •Declaring an Event
- •Subscribing to an Event
- •Unsubscribing from an Event
- •Raising an Event
- •Understanding WPF User Interface Events
- •Using Events
- •Chapter 17 Quick Reference
- •The Problem with objects
- •The Generics Solution
- •Generics vs. Generalized Classes
- •Generics and Constraints
- •Creating a Generic Class
- •The Theory of Binary Trees
- •Building a Binary Tree Class by Using Generics
- •Creating a Generic Method
- •Chapter 18 Quick Reference
- •Enumerating the Elements in a Collection
- •Manually Implementing an Enumerator
- •Implementing the IEnumerable Interface
- •Implementing an Enumerator by Using an Iterator
- •A Simple Iterator
- •Chapter 19 Quick Reference
- •What Is Language Integrated Query (LINQ)?
- •Using LINQ in a C# Application
- •Selecting Data
- •Filtering Data
- •Ordering, Grouping, and Aggregating Data
- •Joining Data
- •Using Query Operators
- •Querying Data in Tree<TItem> Objects
- •LINQ and Deferred Evaluation
- •Chapter 20 Quick Reference
- •Understanding Operators
- •Operator Constraints
- •Overloaded Operators
- •Creating Symmetric Operators
- •Understanding Compound Assignment
- •Declaring Increment and Decrement Operators
- •Implementing an Operator
- •Understanding Conversion Operators
- •Providing Built-In Conversions
- •Creating Symmetric Operators, Revisited
- •Adding an Implicit Conversion Operator
- •Chapter 21 Quick Reference
- •Creating a WPF Application
- •Creating a Windows Presentation Foundation Application
- •Adding Controls to the Form
- •Using WPF Controls
- •Changing Properties Dynamically
- •Handling Events in a WPF Form
- •Processing Events in Windows Forms
- •Chapter 22 Quick Reference
- •Menu Guidelines and Style
- •Menus and Menu Events
- •Creating a Menu
- •Handling Menu Events
- •Shortcut Menus
- •Creating Shortcut Menus
- •Windows Common Dialog Boxes
- •Using the SaveFileDialog Class
- •Chapter 23 Quick Reference
- •Validating Data
- •Strategies for Validating User Input
- •An Example—Customer Information Maintenance
- •Performing Validation by Using Data Binding
- •Changing the Point at Which Validation Occurs
- •Chapter 24 Quick Reference
- •Querying a Database by Using ADO.NET
- •The Northwind Database
- •Creating the Database
- •Using ADO.NET to Query Order Information
- •Querying a Database by Using DLINQ
- •Creating and Running a DLINQ Query
- •Deferred and Immediate Fetching
- •Joining Tables and Creating Relationships
- •Deferred and Immediate Fetching Revisited
- •Using DLINQ to Query Order Information
- •Chapter 25 Quick Reference
- •Using Data Binding with DLINQ
- •Using DLINQ to Modify Data
- •Updating Existing Data
- •Adding and Deleting Data
- •Chapter 26 Quick Reference
- •Understanding the Internet as an Infrastructure
- •Understanding Web Server Requests and Responses
- •Managing State
- •Understanding ASP.NET
- •Creating Web Applications with ASP.NET
- •Building an ASP.NET Application
- •Understanding Server Controls
- •Creating and Using a Theme
- •Chapter 27 Quick Reference
- •Comparing Server and Client Validations
- •Validating Data at the Web Server
- •Validating Data in the Web Browser
- •Implementing Client Validation
- •Chapter 28 Quick Reference
- •Managing Security
- •Understanding Forms-Based Security
- •Implementing Forms-Based Security
- •Querying and Displaying Data
- •Understanding the Web Forms GridView Control
- •Displaying Customer and Order History Information
- •Paging Data
- •Editing Data
- •Updating Rows Through a GridView Control
- •Navigating Between Forms
- •Chapter 29 Quick Reference
- •What Is a Web Service?
- •The Role of SOAP
- •What Is the Web Services Description Language?
- •Nonfunctional Requirements of Web Services
- •The Role of Windows Communication Foundation
- •Building a Web Service
- •Creating the ProductsService Web Service
- •Web Services, Clients, and Proxies
- •Talking SOAP: The Easy Way
- •Consuming the ProductsService Web Service
- •Chapter 30 Quick Reference
104 |
Part I Introducing Microsoft Visual C# and Microsoft Visual Studio 2008 |
Trying Code and Catching Exceptions
C# makes it easy to separate the error handling code from the code that implements the main flow of the program by using exceptions and exception handlers. To write exceptionaware programs, you need to do two things:
1.Write your code inside a try block (try is a C# keyword). When the code runs, it attempts to execute all the statements inside the try block, and if none of the state-
ments generates an exception, they all run, one after the other, to completion. However, if an error condition occurs, execution jumps out of the try block and into another piece of code designed to catch and handle the exception—a catch handler.
2.Write one or more catch handlers (catch is another C# keyword) immediately after the try block to handle any possible error conditions. A catch handler is intended to catch and handle a specific type of exception, and you can have multiple catch handlers after a try block, each one designed to trap and process a specific exception so that you can provide different handlers for the different errors that could arise in the try block. If any one of the statements inside the try block causes an error, the runtime generates and throws an exception. The runtime then examines the catch handlers after the try block and transfers control directly to the first matching handler.
Here’s an example of code in a try block that attempts to convert strings that a user has typed in some text boxes on a form to integer values, call a method to calculate a value, and write the result to another text box. Converting a string to an integer requires that the string
contain a valid representation and not some arbitrary sequence of characters. If the string contains invalid characters, the int.Parse method throws a FormatException, and execution
transfers to the corresponding catch handler. When the catch handler finishes, the program continues with the first statement after the handler:
try
{
int leftHandSide = int.Parse(lhsOperand.Text); int rightHandSide = int.Parse(rhsOperand.Text);
int answer = doCalculation(leftHandSide, rightHandSide); result.Text = answer.ToString();
}
catch (FormatException fEx)
{
// Handle the exception
...
}
Chapter 6 Managing Errors and Exceptions |
105 |
Handling an Exception
A catch handler uses syntax similar to that used by a method parameter to specify the exception to be caught. In the preceding example, when a FormatException is thrown,
the fEx variable is populated with an object containing the details of the exception. The FormatException type has a number of properties that you can examine to determine the
exact cause of the exception. Many of these properties are common to all exceptions. For example, the Message property contains a text description of the error that caused the ex-
ception. You can use this information when handling the exception, perhaps recording the details to a log file or displaying a meaningful message to the user and asking the user to try again.
Unhandled Exceptions
What happens if a try block throws an exception and there is no corresponding catch handler? In the previous example, it is possible that the lhsOperand text box contains the
string representation of a valid integer, but the integer that it represents is outside the range of valid integers supported by C# (for example, “2147483648”). In this case, the int.Parse statement will throw an OverflowException, which will not be caught by the FormatException catch handler. If this occurs, if the try block is part of a method, the method immediately
exits and execution returns to the calling method. If the calling method uses a try block, the
runtime attempts to locate a matching catch handler after the try block in the calling method and execute it. If the calling method does not use a try block, or there is no matching catch
handler, the calling method immediately exits and execution returns to its caller, where the process is repeated. If a matching catch handler is eventually found, the handler runs and execution continues with the first statement after the catch handler in the catching method.
Important Notice that after catching an exception, execution continues in the method
containing the catch block that caught the exception. If the exception occurred in a method other than the one containing the catch handler, control does not return to the method that
caused the exception.
If, after cascading back through the list of calling methods, the runtime is unable to find a matching catch handler, the program terminates with an unhandled exception. If you are
running the application in Microsoft Visual Studio 2008 in debug mode (you selected Start Debugging on the Debug menu to run the application), the following information dialog box
appears and the application drops into the debugger, allowing you to determine the cause of the exception:
106 |
Part I Introducing Microsoft Visual C# and Microsoft Visual Studio 2008 |
|
|
|
|
Using Multiple catch Handlers
The previous discussion highlights how different errors throw different kinds of exceptions to represent different kinds of failures. To cope with these situations, you can supply multiple catch handlers, one after the other, like this:
try
{
int leftHandSide = int.Parse(lhsOperand.Text); int rightHandSide = int.Parse(rhsOperand.Text);
int answer = doCalculation(leftHandSide, rightHandSide); result.Text = answer.ToString();
}
catch (FormatException fEx)
{
//...
}
catch (OverflowException oEx)
{
//...
}
Catching Multiple Exceptions
The exception-catching mechanism provided by C# and the Microsoft .NET Framework is quite comprehensive. Many different exceptions are defined in the .NET Framework, and any
Chapter 6 Managing Errors and Exceptions |
107 |
programs you write will be able to throw most of them! It is highly unlikely that you will want to write catch handlers for every possible exception that your code can throw. So how do you
ensure that your programs catch and handle all possible exceptions?
The answer to this question lies in the way the different exceptions are related to one
another. Exceptions are organized into families called inheritance hierarchies. (You will learn about inheritance in Chapter 12, “Working with Inheritance.”) FormatException and OverflowException both belong to a family called SystemException, as do a number of other
exceptions. Rather than catching each of these exceptions individually, you can create a handler that catches SystemException. SystemException is a member of a family simply called Exception, which is the great-granddaddy of all exceptions. If you catch Exception, the handler
traps every possible exception that can occur.
Note The Exception family includes a wide variety of exceptions, many of which are intended for use by various parts of the .NET Framework. Some of these are somewhat esoteric, but it is still useful to understand how to catch them.
The next example shows how to catch all possible system exceptions:
try
{
int leftHandSide = int.Parse(lhsOperand.Text); int rightHandSide = int.Parse(rhsOperand.Text);
int answer = doCalculation(leftHandSide, rightHandSide); result.Text = answer.ToString();
}
catch (Exception ex) // this is a general catch handler
{
//...
}
Tip If you want to catch Exception, you can actually omit its name from the catch handler because it is the default exception:
catch
{
// ...
}
However, this is not always recommended. The exception object passed in to the catch handler can contain useful information concerning the exception, which is not accessible when using this version of the catch construct.
There is one final question you should be asking at this point: What happens if the same exception matches multiple catch handlers at the end of a try block? If you catch
108 Part I Introducing Microsoft Visual C# and Microsoft Visual Studio 2008
FormatException and Exception in two different handlers, which one will run (or will both execute)?
When an exception occurs, the first handler found by the runtime that matches the excep-
tion is used, and the others are ignored. What this means is that if you place a handler for Exception before a handler for FormatException, the FormatException handler will never run.
Therefore, you should place more-specific catch handlers above a general catch handler after a try block. If none of the specific catch handlers matches the exception, the general catch
handler will.
In the following exercise, you will write a try block and catch an exception.
Write a statement
1.Start Visual Studio 2008 if it is not already running.
2.Open the MathsOperators solution located in the \Microsoft Press\Visual CSharp Step By Step\Chapter 6\MathsOperators folder in your Documents folder.
This is a variation on the program that you first saw in Chapter 2, “Working with Variables, Operators, and Expressions.” It was used to demonstrate the different arithmetic operators.
3.On the Debug menu, click Start Without Debugging.
The form appears. You are now going to enter some text that is deliberately not valid in the left operand text box. This operation will demonstrate the lack of robustness in the current version of the program.
4.Type John in the left operand text box, and then click Calculate.
A dialog box reports an unhandled exception; the text you entered in the left operand text box caused the application to fail.
Note The Debug button does not appear if you are using Microsoft Visual C# 2008
Express Edition.
You might see a different version of this dialog box (shown later) depending on how
you have configured problem reporting in Control Panel. If you see this dialog box, simply click the Close the program link whenever the instructions in the following steps refer to the Close Program button, and click the Debug the program link whenever
Chapter 6 Managing Errors and Exceptions |
109 |
the instructions refer to the Debug button. (If you are using Windows XP rather than Windows Vista, you will see a different dialog box with Debug, Send Error Report, and Don’t Send buttons. Click the Don’t Send button to close the program.)
5.If you are using Visual Studio 2008, click Debug. In the Visual Studio Just-In-Time Debugger dialog box, in the Possible Debuggers list box, select MathsOperators – Microsoft Visual Studio: Visual Studio 2008, and then click Yes:
6.If you are using Visual C# 2008 Express Edition, click Close Program. On the Debug menu, click Start Debugging. Type John in the left operand text box, and then click Calculate.
7.Whether you are using Visual Studio 2008 or Visual C# 2008 Express Edition, the Visual Studio 2008 debugger starts and highlights the line of code that caused the exception and displays some additional information about the exception:
110 |
Part I Introducing Microsoft Visual C# and Microsoft Visual Studio 2008 |
|
|
|
|
|
|
|
You can see that the exception was thrown by the call to int.Parse inside the calculateClick method. The problem is that this method is unable to parse the text
“John” into a valid number.
Note You can view the code that caused an exception only if you actually have the source code available on your computer.
8.On the Debug menu, click Stop Debugging.
9.Display the code for the file Window1.xaml.cs in the Code and Text Editor window, and locate the calculateClick method.
10.Add a try block (including braces) around the four statements inside this method, as shown in bold type here:
try
{
int leftHandSide = int.Parse(lhsOperand.Text); int rightHandSide = int.Parse(rhsOperand.Text);
int answer = doCalculation(leftHandSide, rightHandSide); result.Text = answer.ToString();
}
11. Add a catch block immediately after the closing brace for this new try block, as follows:
catch (FormatException fEx)
{
