Introduction
-
At execution time programs can run into unspecified behaviour, such as
- having to divide by zero,
- having to access an array at an index greater than its length.
-
For example, the following instructions would compile just fine, but at execution time the program would “explode”:
- In the first case, a “System.DivideByZeroException has been thrown” error message would be displayed.
- In the second case, a “System.IndexOutOfRangeException has been thrown” error message would be displayed.
- Those are examples of exceptions thrown by operations.
-
Methods can also throw exceptions. For example, the following statement:
will display a “System.FormatException has been thrown” error message. This is because the
Parse
method can throw an exception. -
Of course, a programmer would not purposely introduce such strange instructions in their code, but they may arise after interacting with the “outside world”, that is, a user, file, or other external factor.
-
C# allows exception handling, which are ways of recovering when such exceptions are thrown, so that the program can keep on executing. Stated differently, they instruct the program what to do, for example, if it is asked to perform a division by 0. This is handled by
catch
blocks. -
C# also allows
finally
block, which contain code executed unconditionally, that is, regardless of if the exception was thrown or not.
Syntax and Rules for try
…catch
…finally
Statements
-
In a first approximation, the syntax of a
try
…catch
…finally
statement is as follows: -
When executed,
<statement block 1>
will be executed statement by statement. If, at any point, one of the statement in<statement block 1>
throws an exception, then the rest of the statements in<statement block 1>
will be discarded and<statement block 2>
will execute. After all of the statements in<statement block 1>
have executed, or after all of the statements in<statement block 2>
have executed, then<statement block 3>
will execute. -
A simple example is
- If the user enters a string that contains only numbers (say, “10”), then the program will display “Your number is 10.” then “Did it worked?“.
- If the user enters a string that does not contain only numbers (say, “No”), then the program will display “Something was off.” then “Did it worked?“.
-
Both the
catch
and thefinally
parts of the statement are optional: they can be both present, or only one can occur in thetry
block statement (however, you have to have one or the other). -
A
try
block can have multiplecatch
, as follows:- This allows a more fine-grained handling of the exceptions that can be thrown.
- In the example, if a
DivideByZeroException
exception is thrown, it is because the user entered “0” and the operation{10 / uInput}
failed. In this case, we can display an appropriate error message (“You tried to divide by zero”). - In the example, if a
FormatException
exception is thrown, it is because the user entered a string containing non-numerical characters, and we can similarly return an appropriate error message. - Writing
catch{…}
is the same ascatch (Exception){…}
: by default, acatch
block catches all the exceptions that can be thrown, not the exceptions of a particular class. Note that, if specifying multiplecatch
blocks, the order matter, as acatch (Exception)
, if placed first, will always execute before thecatch
blocks put after.
Exception Class and Objects
-
Technically speaking, an exception is an object in a particular class that inherits from the exception class.
-
We can assign an identifier to it in the
catch
block, to be able to access some of its properties such as theMessage
and aStackTrace
properties. -
For example, the
IndexOutOfRangeException
object returned when trying to access an array outside of its bound can be namedex
and used to display particular information:- When the statement
test[10] = 10;
gets executed, the exception is thrown, namedex
, and we display its message (“Index was outside the bounds of the array.”) and StackTrace (“at Program.Main (System.String[] args) [0x0000f] in<path>
/Program.cs:<line>
”, with<path>
the path of the Program.cs file, and<line>
the line where the error occurs).
- When the statement
Purpose of the finally
Block
-
The difference between
and
is that in the second case,
<statement block 3>
may be skipped if<statement block 1>
or<statement block 2>
return a value, throw an exception that is not caught, or break the flow of control (using for examplebreak;
). In the first case,<statement block 3>
will always1 get executed, no matter which block gets executed and even if it breaks the control flow or throws another exception. -
For example,
will always display “Thank you for playing!“. If this last statement was not in the
finally
block, but was simply inserted after thetry
…catch
statement, then this message would actually never be displayed.
Scoping in try
… catch
… finally
Statements {#scoping-in-try-catch-finally-statements}
-
Understanding the scope of statements in
try
…catch
…finally
statements can be tricky. -
The general rules are:
- Variables declared in
try
,catch
orfinally
blocks will not be accessible outside of them, - Variables whose value are set in the
try
block will keep the value they had when thetry
block threw an exception.
- Variables declared in
-
For example, in the following code,
-
This program will display
-
The variable
x
would not be accessible to thecatch
orfinally
blocks. -
If we were to remove the
zero = 0;
statement, then the program would display “The variable holds 2.”.
-
When To Use try
… catch
and When To Use TryParse
? {#when-to-use-try-catch-and-when-to-use-tryparse}
-
If something goes wrong in a method, that method can either return some error code or throw an exception.
-
Returning an error code means possibly cluttering the signature of the method with some extra parameters, as in the
TryParse
methods. -
TryParse
is “baking in” a way of signaling that something went wrong because- This type of error is simple, common and predictable,
- It decided not to care about why the parsing fails (it can be
either because the input is
null
, because it is not in valid format, or because it produces an overflow).
-
However, exceptions can handle those cases differently thanks to different
catch
blocks: -
So, in summary,
TryParse
is in general better if there is no need to handle the different exceptions differently.
Throwing an Exception
-
You can explicitly throw an exception by
- Creating an
Exception
object, - Throwing it, using the
throw
keyword.
- Creating an
-
For example, the property setter in the following class can explicitly throw an
ArgumentOutOfRangeException
object if we try to create aCircle
object with a negative diameter: -
To use this class properly, every time the
Diameter
value is set (using the set accessor, possibly via the constructor), atry
…catch
statement should be used to handle a possible exception, with possibly a loop around it, as follows: -
In the last
catch
block above, thethrow;
(without argument) will pass the exception to the calling environment. It is indeed possible to catch the exception, do something with it (typically, log it or display an error message), and then “pass” that exception to the surrounding environment.
Footnotes
-
That is, unless the program crashes or loops forever. ↩