As much as we’d like to write perfect programs, there’s going to be a point where you need to think about handling error cases in your C# applications. That’s why you’re here: for an introduction to try catch in C#!
In this article, I’ll explain the very basics of how to structure try catch blocks and what each block is responsible for. The code is simple and aims to help explain some of the basic concepts for beginners working in C#. If that sounds like you, read on!
Let’s face it: there are going to be exceptions in our C# applications and services at some point. We hate to admit it, but it’s a reality that we have to understand how to work with.
Exception handling in C# is done by using what’s called a try/catch block. So try catch in C# is how we set up code to have some type of protection and define the logic for how we want to deal with those exceptions.
The following sections will explain the two main pieces, try and catch, and later on we’ll see an extra third component we can add to the try catch blocks we create in C#.
You can follow along with this video on how to handle exceptions with try/catch blocks in C# as well:
The try block is the first important part of our exception handling that we need to look at. It serves the purpose of enclosing the code that might potentially throw an exception. By placing the code within a try block, we ensure that any exceptions that occur can be caught and handled appropriately — but it’s not handled by this block, this block just tells us what will get handled.
The try block is structured using the keyword “try”, followed by a set of curly braces that contain the code we want to monitor for exceptions. This code is often referred to as the “protected code”, as it is within this block that we anticipate will be potentially throwing exceptions.
Here’s an example of a try block in C#:
try
{
// Code that might throw an exception
int result = Divide(10, 0);
Console.WriteLine("The result is: " + result);
}
In this example, we have a function called “Divide” that takes two integers as parameters and attempts to perform a division operation. However, if the second parameter is zero, it will throw a divide by zero exception. By enclosing this code within a try block, we can catch and handle this exception appropriately — but we need another block to actually handle it! And that’s where we’re headed in the next section!
The main purpose of the catch block is to provide a mechanism for handling exceptions gracefully. The try block allows us to define what we want to protect, and the catch block allows us to define what we’ll handle and how we’ll handle it.
When an exception is thrown, the catch block allows us to specify the actions to be taken in response to that exception. These actions could include logging the exception, displaying an error message to the user, or taking any other necessary corrective measures. The choice is yours!
To catch a specific type of exception, we use the catch keyword followed by the exception type we want to handle. This allows us to handle different exceptions in different ways, tailoring our response based on the specific type of exception.
Here’s an example of a catch block that handles a specific exception type, in this case, the DivideByZeroException
:
try
{
int result = 10 / 0; // This will throw a DivideByZeroException
}
catch (DivideByZeroException ex)
{
Console.WriteLine("An error occurred: " + ex.Message);
}
In this example, the code within the try block attempts to divide the number 10 by 0, which will result in a DivideByZeroException
. The catch block catches this exception and executes the code within its block, which in this case, simply writes an error message to the console, displaying the message provided by the exception.
You can also use a base exception class or omit the exception portion altogether to catch all (well, almost all) exceptions:
try
{
}
catch (Exception ex)
{
// this will catch all catchable exceptions
}
// OR
try
{
}
catch
{
// note that we don't have an exception instance in this case!
}
If one exception can be thrown from a called piece of code, odds are that many can. As a result, it’s common to encounter scenarios where multiple types of exceptions can occur within a single try block. These exceptions may arise due to different errors or exceptional situations that need to be handled appropriately. In this section, I’ll discuss how to handle multiple exceptions by using either multiple catch blocks or a single catch block with multiple exception types. Let’s see how!
When handling multiple exceptions, one approach is to use multiple catch blocks. Each catch block is responsible for handling a specific type of exception. By using this approach, you can specify separate code blocks to handle different exceptions based on their respective types.
Here is an example that demonstrates the multiple catch blocks approach:
try
{
// Code that may throw exceptions
}
catch (ArgumentNullException ex)
{
// Handle ArgumentNullException
}
catch (DivideByZeroException ex)
{
// Handle DivideByZeroException
}
catch (FormatException ex)
{
// Handle FormatException
}
In the above example, the catch block following the try block will catch and handle any ArgumentNullException
that occurs. Similarly, the catch blocks for DivideByZeroException
and FormatException
will handle their respective exceptions.
Another approach for handling multiple exceptions is to use a single catch block that can handle multiple exception types. This approach can be useful when you want to handle multiple exceptions in the same way without writing separate catch blocks for each one.
Here is an example that demonstrates the single catch block approach:
try
{
// Code that may throw exceptions
}
catch (Exception ex) when // look at this fancy keyword!
(
ex is ArgumentNullException ||
ex is DivideByZeroException ||
ex is FormatException
)
{
// Handle ArgumentNullException, DivideByZeroException, and FormatException
}
In the above example, the catch block will handle any ArgumentNullException
, DivideByZeroException
, or FormatException
that occurs within the try block. This is because we’ve placed a filter onto the exception handler using the when
keyword!
I should note that you can do more filtering than just type-checking with the when keyword. You’re able to examine the different properties for the exception instance and create filters accordingly. If you needed to filter by a certain exception message or error code on a particular exception type, you can do that with the when
keyword.
The finally block in C# is used to define a section of code that will always be executed, regardless of whether an exception occurred or not. Its purpose is to ensure that certain tasks, such as releasing resources or performing cleanup operations, are always carried out, regardless of the outcome of the try block.
The finally block is executed after the try and catch blocks, whether an exception is thrown or not. It’s written with the keyword “finally” followed by a set of curly braces {}. Any code within these braces will be executed immediately after the catch block finishes.
One common use case for the finally block is to release any resources that were acquired within the try block. This could include closing file handles, database connections, network sockets, or any other resource that needs to be freed up. By placing the cleanup code in the finally block, you can ensure that it will be executed even if an exception is thrown in the try block.
Here is an example that demonstrates the use of the finally block for resource cleanup:
try
{
// Code that could potentially throw an exception
// Acquiring resources
// Performing operations
}
catch(Exception ex)
{
// Exception handling code
// Logging or displaying error messages
}
finally
{
// Code to release resources
// Closing file handles, database connections, etc.
}
In this example, if an exception is thrown in the try block, the program flow will jump to the catch block to handle the exception. However, regardless of whether an exception is thrown or not, the code within the finally block will always be executed afterwards. This ensures that any acquired resources are properly released, preventing resource leaks and maintaining the stability of the program.
The finally block that we can use when it comes to try catch in C# is very helpful — but try/catch syntax is already a bit clunky to begin with. It’s a common pattern that we want to clean up resources and if we’re being defensive, we want to make sure that we clean them up even when we have error scenarios.
We can use the IDisposable
interface and a using block to help support this. So instead of requiring a finally block to clean things up, you could have code inside of a Dispose()
method on the object that implements IDisposable
. The code would be simplified to this:
public class MyClass : IDisposable
{
public void Dispose()
{
// TODO: do your cleanup... same
// stuff that you might have wanted
// in your finally block!
}
}
using (Myclass myClass = new())
{
// do stuff with myClass
}
// dispose runs after we leave the block
This is a lot less clunky, in my opinion than a whole try/finally block to support cleanup — but of course, this will not catch errors just ensure you can still run cleanup code. We can simplify this further with implicit using now where the Dispose()
method runs when the variable goes out of scope:
public void SomeMethod()
{
// implicit using...
using Myclass myClass = new();
// do stuff with myClass
// dispose runs at the end here since the variable goes out of scope
}
Now that you’ve seen several different ways we can use try catch in C# as well as some different use cases, let’s talk about some considerations. We’ve seen the tools we have to use, but some guidance on using them appropriately seems fitting:
Now you’ve had a chance to see the basics of try catch in C#! I’ve aimed to help familiarize you with the purpose of the different blocks, and introduced the finally block as well! With some basic code examples to refer to along with some extra considerations to keep in mind when programming, you’re all set to start catching exceptions in your code!
If you found this useful and you’re looking for more learning opportunities, consider subscribing to my free weekly software engineering newsletter and check out my free videos on YouTube!