4.6 Try Statements and Exceptions
try statement-block
[catch (exception type value?)?
statement-block]+ |
finally statement-block |
[catch (exception type value?)?
statement-block]+
finally statement-block
The purpose of a try statement
is to simplify program execution in
exceptional circumstances—typically, an error. A
try statement does two things. First, it lets the
catch block catch exceptions
thrown during the
try block's execution. Second, it
ensures that execution cannot leave the try block
without first executing the finally block. A
try block must be followed by a
catch block(s), a finally
block, or both. The form of a try block looks like
this:
try {
... // exception may be thrown during execution of this function
}
catch (ExceptionA ex) {
... // react to exception of type ExceptionA
}
catch (ExceptionB ex) {
... // react to exception of type ExceptionB
}
finally {
... // code to always run after try block executes, even if
... // an exception is not thrown
}
4.6.1 Exceptions
C# exceptions are objects that
contain
information representing the occurrence of an exceptional program
state. When an exceptional state has occurred (e.g., a method
receives an illegal value), an exception object may be thrown, and
the call-stack is unwound until the exception is caught by an
exception-handling block. For example:
using System;
namespace TryStatementsAndExceptions {
public class WeightCalculator {
public static float CalcBMI (float weightKilos, float metersTall) {
if (metersTall < 0 || metersTall > 3)
throw new ArgumentException ("Impossible Height", "metersTall");
if (weightKilos < 0 || weightKilos > 1000)
throw new ArgumentException ("Impossible Weight", "weightKilos");
return weightKilos / (metersTall*metersTall);
}
}
class Test {
static void Main ( ) {
TestIt ( );
}
static void TestIt ( ) {
try {
float bmi = WeightCalculator.CalcBMI (100, 5);
Console.WriteLine(bmi);
}
catch(ArgumentException ex) {
Console.WriteLine(ex);
}
finally {
Console.WriteLine ("Thanks for running the program");
}
Console.Read( );
}
}
}
In this example, calling CalcBMI throws an
ArgumentException indicating that
it's impossible for someone to be
five
meters tall. Execution leaves CalcBMI and returns
to the calling method, TestIt, which handles the
ArgumentException, and displays the exception to
the Console. Next, the finally method is executed,
which prints "Thanks for running the
program" to the Console. Without our
try statement, the call stack would have been
unwound right back to the Main method, and the
program would terminate.
4.6.2 The catch Clause
A catch clause specifies the
exception type
(including derived types) to catch. An exception must be of type
System.Exception, or a type that derives from
System.Exception. Catching
System.Exception provides the widest possible net
for catching errors, which is useful if
your
handling of the error is totally generic, such as an error-logging
mechanism. Otherwise, you should catch a more specific exception
type, to prevent your catch block from having to
deal with a circumstance it wasn't designed to
handle (e.g., an out-of-memory exception).
4.6.2.1 Omitting the exception variable
Specifying only an exception
type without
a variable name allows an exception
to be caught when we don't need to use the exception
instance and merely knowing its type is enough. The previous example
could be written like this:
catch(ArgumentException) { // don't specify variable
Console.WriteLine("Couldn't calculate ideal weight!");
}
4.6.2.2 Omitting the catch expression
You may also entirely omit
the catch
expression. This catches an exception of any type, even types thrown
by other non-CLS-compliant languages that are not derived from
System.Exception. The previous example could be
written like this:
catch {
Console.WriteLine("Couldn't calculate ideal weight!");
}
4.6.2.3 Specifying multiple catch clauses
When declaring multiple
catch clauses, only the first
catch clause with an exception type that matches
the thrown exception executes its catch block. It
is illegal for an exception type B to precede an
exception type D if B is a
base class of D, since it would be unreachable.
try {...}
catch (NullReferenceException) {...}
catch (ArgumentException) {...}
catch {...}
4.6.3 The finally Block
A finally block is always
executed when control leaves the
try block. A finally block is
executed at any of the following periods:
Immediately after the try block completes Immediately after the try block prematurely exits
with a jump statement (e.g., return,
goto), and immediately before the target of the
jump statement Immediately after a catch block executes
finally blocks can add determinism to a
program's execution by ensuring that the specified
code always gets executed.
In our main example, if the height passed to the calculator is
invalid, an ArgumentException is thrown that
executes the catch block, followed by the
finally block. However, if anything else goes
wrong, the finally block is still executed. This
ensures that we say goodbye to our user before exiting the program.
4.6.4 Key Properties of System.Exception
Notable properties
of
System.Exception include:
- StackTrace
-
A string representing all the methods
that are called from the origin of the exception to the
catch block.
- Message
-
A string with a description of the
error.
- InnerException
-
This cascading exception structure can be
particularly useful when debugging. Sometimes it is useful to catch
an exception, then throw a new, more specific exception. For
instance, we may catch an IOException, and then
throw a ProblemFooingException that contains more
specific information on what went wrong. In this scenario, the
ProblemFooingException should include the
IOException as the
InnerException argument in its constructor, which
is assigned to the InnerException property.
|
Note that in C# all exceptions are runtime exceptions—there is
no equivalent to Java's compile-time checked
exceptions.
|
|
|