Try...Catch...Finally
Explore how to implement robust exception handling in C# using try, catch, and finally blocks. Learn to manage runtime errors like out-of-range array access, handle specific exceptions, and ensure resource cleanup with finally blocks. Understand required syntax rules and advanced patterns to write more reliable and maintainable code.
We'll cover the following...
Program errors are unavoidable. They can happen because our application is trying to reach a server that is not responding, because we run out of computer memory, or because our program is trying to read a non-existent file. In any case, it is likely that an exception occurs.
Exceptions are program states where the application can no longer continue to run normally, so we have to address them.
Consider the following example. We create an array of size four, but we attempt to access the fifth element (which does not exist). This produces an exception.
Line 2: We initialize an integer array named
numberswith four elements.Line 6: We attempt to print the element at index
4. Since arrays are zero-indexed, the valid indices are 0 through 3. This line triggers a runtime error.
The result of execution is an unhandled exception of type IndexOutOfRangeException. This exception halts execution because the code tried to access an array index outside of its allocated bounds.
Exception handling in C#
The try, catch, and finally blocks allow us to run our code, handle an exception if it occurs, and perform cleanup operations.
try{// Code that may generate an exception}catch{// Actions to take in case an exception occurs}
When working with arrays, it is reasonable to expect that exceptions might occur. We use the try and catch blocks to prevent the program from crashing.
Line 1: We start the
tryblock. The program attempts to execute the code inside this block.Line 4: This line triggers an exception because index
4is out of bounds.Line 6: The program immediately jumps to the
catchblock when the error occurs.Line 8: We print a friendly message to the console instead of the program crashing.
Instead of terminating, our program executes without errors.
We can define specific catch blocks to handle different types of exceptions.
Line 6: We specify
IndexOutOfRangeException. If the error inside thetryblock matches this type, this block executes.Line 10: This
catchblock uses the baseExceptiontype. It acts as a fallback to catch any other errors that are notIndexOutOfRangeException.Line 12: We access the
Messageproperty of the exception objectexto print the system’s error description.
The finally block
The finally block is optional, but it is useful when our code works with external resources and connections that must be closed after we are done working with them.
Note: If our code terminates with an exception while an external connection (such as a database connection) is open and we fail to close it, it will lead to memory leaks.
No matter what happens in our try and catch blocks, the finally block will run.
Line 6: The specific
IndexOutOfRangeExceptionis caught here.Line 14: The
finallyblock executes regardless of whether an exception occurred or not.Line 16: This code runs even if the
tryblock finishes successfully or if acatchblock handles an error. This ensures resources are always released.
Mandatory block requirements
C# enforces strict syntactical rules for these blocks:
Association with
try: Acatchorfinallyblock cannot exist independently; they must immediately follow atryblock.Minimum requirement: We cannot omit both the
catchandfinallyblocks. Atrystatement must be accompanied by at least one of them to be valid.Specific order: When using all three, the sequence must always be
try, followed bycatch, and ending withfinally.
The try-finally pattern
You do not always have to include a catch block. Sometimes, you want an exception to move up to a different part of the program (propagate), but you still need to ensure that resources (like a file or a database connection) are closed properly.
Line 1: We initiate a
tryblock to monitor code execution.Line 5: We throw an exception manually. Since there is no
catchblock here, the exception is unhandled in this method and will “bubble up” to the caller.Line 7: We define the
finallyblock, which is guaranteed to run even if the program is about to crash or propagate an error.Line 10: This cleanup code executes to prevent resource leaks before the exception moves up the call stack.
If we use try...finally without a catch block, the exception remains unhandled in the current location. Once the finally block finishes, the runtime propagates the error up the call stack to find a matching catch block in a parent method. If no handler is found anywhere in the program, the application will terminate.