Exception Handling in C#: try-catch-finally, throw & Custom Exceptions
1. Exception Handling in C#
Q: What is exception handling in C#?
Exception handling is a mechanism to handle runtime errors (exceptions) gracefully, preventing program crashes and allowing recovery or logging. In C#, exceptions are objects derived from the System.Exception class, thrown when errors occur (e.g., division by zero, file not found).
Q: Why is exception handling important?
- Prevents program crashes by catching and handling errors.
- Improves robustness by providing alternative execution paths.
- Enables logging and user-friendly error messages.
- Supports cleanup of resources (e.g., files, connections) to avoid leaks.
Q: How does exception handling in C# differ from C/C++?
- C#: Managed, uses
try,catch,finally, andthrowwith strongly-typedExceptionobjects. Integrated with .NET, supports garbage collection. - C++: Uses
try,catch, but exceptions can be any type (not just objects). Manual memory management, nofinally. - C: No built-in exception handling, uses error codes or manual checks.
- C# Advantage: Type-safe, structured, with
finallyandusingfor resource management.
2. try, catch, and finally
Q: What is the try block in C#?
The try block encloses code that might throw an exception. If an exception occurs, control transfers to a matching catch block.
Syntax:
try {
// Code that might throw an exception
}
Q: What is the catch block in C#?
The catch block handles exceptions thrown in the try block. It can specify an exception type to catch specific errors or use a general Exception to catch all. Multiple catch blocks can handle different exception types.
Syntax:
catch (ExceptionType ex) {
// Handle exception
}
Q: What is the finally block in C#?
The finally block executes regardless of whether an exception is thrown or caught, ensuring cleanup (e.g., closing files). It is optional and follows try or catch.
Syntax:
finally {
// Cleanup code
}
Q: What is the throw statement in C#?
The throw statement raises an exception explicitly, either a new exception or rethrowing a caught one.
Syntax:
throw new Exception("Error message");
Q: Can you give an example of try, catch, and finally in C#?
using System;
namespace ExceptionHandling
{
class Program
{
static void Main(string[] args)
{
try
{
int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[5]); // Throws IndexOutOfRangeException
int result = 10 / 0; // Throws DivideByZeroException
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine($"Index error: {ex.Message}");
}
catch (DivideByZeroException ex)
{
Console.WriteLine($"Division error: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"General error: {ex.Message}");
}
finally
{
Console.WriteLine("Cleanup in finally block.");
}
}
}
}
Output:
Index error: Index was outside the bounds of the array.
Cleanup in finally block.
3. Custom Exceptions
Q: What are custom exceptions in C#?
Custom exceptions are user-defined exception classes that inherit from System.Exception (or a derived class like ApplicationException). They allow developers to create specific error types for their application, improving clarity and handling.
Syntax:
class CustomException : Exception {
public CustomException(string message) : base(message) { }
}
Q: When should you use custom exceptions?
- Represent specific error conditions in your application (e.g.,
InvalidUserInputException). - Provide detailed error information or additional properties.
- Improve code readability and maintainability by distinguishing error types.
Q: Can you give an example of a custom exception in C#?
using System;
namespace CustomExceptionExample
{
// Custom exception class
class InsufficientBalanceException : Exception
{
public decimal CurrentBalance { get; }
public decimal RequiredAmount { get; }
public InsufficientBalanceException(string message, decimal balance, decimal amount)
: base(message)
{
CurrentBalance = balance;
RequiredAmount = amount;
}
}
class BankAccount
{
private decimal balance;
public BankAccount(decimal initialBalance)
{
balance = initialBalance;
}
public void Withdraw(decimal amount)
{
if (amount <= 0)
{
throw new ArgumentException("Withdrawal amount must be positive.");
}
if (amount > balance)
{
throw new InsufficientBalanceException(
$"Cannot withdraw {amount:C}. Insufficient funds.",
balance, amount);
}
balance -= amount;
Console.WriteLine($"Withdrew {amount:C}. New balance: {balance:C}");
}
}
class Program
{
static void Main(string[] args)
{
BankAccount account = new BankAccount(100.50m);
try
{
account.Withdraw(50m); // Valid withdrawal
account.Withdraw(200m); // Throws custom exception
}
catch (InsufficientBalanceException ex)
{
Console.WriteLine($"Error: {ex.Message}");
Console.WriteLine($"Balance: {ex.CurrentBalance:C}, Requested: {ex.RequiredAmount:C}");
}
catch (ArgumentException ex)
{
Console.WriteLine($"Argument error: {ex.Message}");
}
finally
{
Console.WriteLine("Transaction processing completed.");
}
}
}
}
Output:
Withdrew $50.00. New balance: $50.50
Error: Cannot withdraw $200.00. Insufficient funds.
Balance: $50.50, Requested: $200.00
Transaction processing completed.
4. Common Mistakes & Best Practices
Q: Common mistakes?
try/catch/finally:
- Catching general
Exceptionwithout specific types, hiding errors. - Empty
catchblocks, ignoring errors without logging. - Forgetting
finallyorusingfor resource cleanup. - Rethrowing incorrectly (e.g.,
throw exinstead ofthrowto preserve stack trace).
Custom Exceptions:
- Not inheriting from
Exceptionor a derived class. - Omitting meaningful properties or messages in custom exceptions.
- Overusing custom exceptions for trivial errors.
Q: Best practices?
try/catch/finally:
- Catch specific exceptions before general
Exception. - Use
finallyorusingfor resource cleanup (e.g., files, connections). - Log exceptions with details (e.g.,
ex.Message,ex.StackTrace). - Avoid empty
catchblocks; handle or rethrow exceptions.
Custom Exceptions:
- Inherit from
Exceptionor a specific base likeApplicationException. - Add properties for context (e.g.,
CurrentBalancein the example). - Provide clear, descriptive messages.
- Use custom exceptions for application-specific errors, not general ones.
General:
- Use
try-catchonly for exceptional cases, not control flow. - Prefer
usingforIDisposableobjects to ensure cleanup. - Validate inputs to prevent exceptions where possible.
- Document exception-throwing methods with XML comments (e.g.,
<exception>). - Leverage modern C# features (e.g.,
whenclause incatchfor filtering).