This content originally appeared on DEV Community and was authored by mohamed Tayel
Meta Description: Learn how to effectively handle exceptions in C# using try/catch blocks. Understand the importance of ordering catch blocks, why Exception
should be placed last, and practice with assignments from easy to difficult levels to master error handling.
In software development, errors are inevitable. No matter how well we plan, test, or try to prevent them, our applications will encounter unexpected situations that can cause failures. These errors, known as exceptions, can occur during runtime and disrupt the normal flow of a program, potentially leading to crashes. Imagine a scenario: a user provides an unexpected input, such as dividing a number by zero. Such actions lead to a runtime exception, as division by zero is undefined.
Exceptions can also be caused by external factors, such as a file permission issue, database connectivity problems, or operating system restrictions. In this article, we'll discuss how C# provides a structured way to handle exceptions using the try/catch block to ensure errors are handled gracefully without crashing the application.
The Try/Catch Block in C#
C# provides a try/catch mechanism to handle exceptions. By placing potentially problematic code inside a try
block, we can catch any exceptions in the catch
block and deal with them appropriately. Here’s the syntax:
try
{
// Code that might throw an exception
}
catch (Exception ex)
{
// Code to handle the exception
}
The try
block contains the code that you think might throw an exception, while the catch
block handles it if an exception is thrown. This approach ensures the application doesn't crash, and we can gracefully inform users or take corrective actions.
Ordering Catch Blocks and Using Exception
One key point to remember when using multiple catch
blocks is the order in which they are written. In C#, exceptions form a hierarchy, with Exception
being the base class for all exceptions. If you use multiple catch
blocks, you need to sort them from the most specific exception type to the most general exception type. This is because once a catch
block matches an exception, the subsequent catch
blocks will not be executed.
Why Place Exception
Last?
The Exception
class is the base class for all exceptions, meaning it can catch any type of exception. If you put it first, the subsequent, more specific catch
blocks would never be reached, because Exception
would handle all errors before they had the chance to be caught by a more specific handler. Placing Exception
last ensures that the most appropriate handler is used first.
Example: Ordering Catch Blocks
try
{
Console.Write("Enter a number: ");
int number = int.Parse(Console.ReadLine());
Console.Write("Enter divisor: ");
int divisor = int.Parse(Console.ReadLine());
int result = number / divisor;
Console.WriteLine($"Result: {result}");
}
catch (DivideByZeroException)
{
Console.WriteLine("Error: Division by zero is not allowed.");
}
catch (FormatException)
{
Console.WriteLine("Error: Please enter a valid number.");
}
catch (Exception ex)
{
Console.WriteLine("An unexpected error occurred: " + ex.Message);
}
In this example:
-
Specific exceptions (
DivideByZeroException
andFormatException
) are caught first. - The general
Exception
is caught last, which acts as a "catch-all" to handle any unforeseen exceptions.
This ordering ensures that:
- If a specific type of exception is thrown (e.g.,
DivideByZeroException
), the correspondingcatch
block handles it directly. - The general
Exception
block will only be used if the thrown exception does not match any specific type above it.
Example: Exception Handling with File Operations
Let’s consider a file processing application where we need to read a configuration file. The application should handle scenarios where the file is missing, inaccessible, or empty:
try
{
Console.Write("Enter the path of the configuration file: ");
string filePath = Console.ReadLine();
if (string.IsNullOrWhiteSpace(filePath))
{
throw new ArgumentNullException("File path cannot be empty.");
}
string content = System.IO.File.ReadAllText(filePath);
if (string.IsNullOrWhiteSpace(content))
{
throw new InvalidOperationException("Configuration file is empty.");
}
Console.WriteLine("File content:");
Console.WriteLine(content);
}
catch (System.IO.FileNotFoundException)
{
Console.WriteLine("Error: The file was not found. Please check the file path.");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Error: You do not have permission to access this file.");
}
catch (ArgumentNullException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine("An unexpected error occurred: " + ex.Message);
}
In this example, we order the catch blocks:
- First, we catch file-specific exceptions (
FileNotFoundException
,UnauthorizedAccessException
). - Then, we handle more general exceptions (
ArgumentNullException
,InvalidOperationException
). - Lastly, we use the
Exception
block to catch any other unexpected errors.
Assignments to Practice Exception Handling
Here are assignments at different difficulty levels to help you understand and apply these concepts.
Assignment Levels
1. Easy Level: Temperature Converter
Create an application that:
- Asks the user to enter a temperature in Celsius.
- Converts it to Fahrenheit.
- If the input is not a valid number, catch the appropriate exception and prompt the user to enter a valid number.
Example:
try
{
Console.Write("Enter temperature in Celsius: ");
double celsius = double.Parse(Console.ReadLine());
double fahrenheit = (celsius * 9 / 5) + 32;
Console.WriteLine($"Temperature in Fahrenheit: {fahrenheit}");
}
catch (FormatException)
{
Console.WriteLine("Error: Please enter a valid number.");
}
catch (Exception ex)
{
Console.WriteLine("An unexpected error occurred: " + ex.Message);
}
2. Medium Level: Employee Data Management
Write a program that:
- Asks the user to enter employee details (Name, Age, Salary).
- Validates the data: age must be between 18 and 65, and salary must be positive.
- Throws an exception if the input is invalid and catches it to display an appropriate message.
Example:
try
{
Console.Write("Enter employee name: ");
string name = Console.ReadLine();
Console.Write("Enter employee age: ");
int age = int.Parse(Console.ReadLine());
if (age < 18 || age > 65)
{
throw new ArgumentOutOfRangeException("Age must be between 18 and 65.");
}
Console.Write("Enter employee salary: ");
decimal salary = decimal.Parse(Console.ReadLine());
if (salary <= 0)
{
throw new ArgumentOutOfRangeException("Salary must be positive.");
}
Console.WriteLine($"Employee {name}, Age: {age}, Salary: {salary:C}");
}
catch (FormatException)
{
Console.WriteLine("Error: Please enter a valid number for age or salary.");
}
catch (ArgumentOutOfRangeException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine("An unexpected error occurred: " + ex.Message);
}
3. Difficult Level: Product Inventory Management
Create an inventory management application:
- Allows adding, removing, and viewing products.
- Each product has a name, price, and quantity.
- Throws a custom
ProductNotFoundException
if the user attempts to remove a product that doesn’t exist. - Handles
ArgumentOutOfRangeException
if an invalid quantity or price is entered.
Example:
public class ProductNotFoundException : Exception
{
public ProductNotFoundException(string productName)
: base($"Product '{productName}' was not found in inventory.") { }
}
try
{
var inventory = new Dictionary<string, (decimal Price, int Quantity)>();
Console.WriteLine("Add a product:");
Console.Write("Product name: ");
string productName = Console.ReadLine();
Console.Write("Product price: ");
decimal price = decimal.Parse(Console.ReadLine());
if (price <= 0)
{
throw new ArgumentOutOfRangeException("Price must be greater than zero.");
}
Console.Write("Product quantity: ");
int quantity = int.Parse(Console.ReadLine());
if (quantity < 0)
{
throw new ArgumentOutOfRangeException("Quantity cannot be negative.");
}
inventory[productName] = (price, quantity);
Console.WriteLine($"Product '{productName}' added with price {price:C} and quantity {quantity}.");
Console.WriteLine("Remove a product:");
Console.Write("Enter product name to remove: ");
string productToRemove = Console.ReadLine();
if (!inventory.ContainsKey(productToRemove))
{
throw new ProductNotFoundException(productToRemove);
}
inventory.Remove(productToRemove);
Console.WriteLine($"Product '{productToRemove}' removed from inventory.");
}
catch (FormatException)
{
Console.WriteLine("Error: Please enter a valid number for price or quantity.");
}
catch (ArgumentOutOfRangeException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
catch (ProductNotFoundException ex)
{
Console.WriteLine(ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("An unexpected error occurred: " + ex.Message);
}
Summary
Exception handling is crucial for developing robust and user-friendly applications. With the use of try/catch blocks, we can prevent our application from crashing and provide meaningful feedback to users when something goes wrong. By ordering catch blocks properly—placing the most specific exceptions first and the general Exception
last—we ensure that our code handles errors appropriately and predictably. Practice these examples to gain a better understanding of handling errors gracefully in C#.
This content originally appeared on DEV Community and was authored by mohamed Tayel
mohamed Tayel | Sciencx (2024-10-02T20:07:01+00:00) Mastering C# Fundamentals: Exception Handling. Retrieved from https://www.scien.cx/2024/10/02/mastering-c-fundamentals-exception-handling/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.