trystatement-block
[catch (exception type value
?)?statement-block
]+ | finallystatement-block
| [catch (exception type value
?)?statement-block
]+ finallystatement-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 }
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.
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).
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!"); }
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!"); }
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 {...}
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 completesImmediately after the
try
block prematurely exits with a jump statement (e.g.,return
,goto
), and immediately before the target of the jump statementImmediately 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.
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
-
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 aProblemFooingException
that contains more specific information on what went wrong. In this scenario, theProblemFooingException
should include theIOException
as theInnerException
argument in its constructor, which is assigned to theInnerException
property.
Get C# in a Nutshell, Second Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.