C# > Advanced C# > Exception Handling > Custom Exceptions
Custom Exception: InsufficientFundsException
This snippet demonstrates how to create and use a custom exception class, InsufficientFundsException
, to handle specific errors related to insufficient funds in a banking scenario. It inherits from the base Exception
class and adds a custom property to hold the amount of the deficiency. Proper exception handling makes code robust and maintainable.
Defining the Custom Exception
This code defines the InsufficientFundsException
class. It inherits from System.Exception
, allowing it to behave like a standard exception. It includes a Deficiency
property to store the amount of money by which the transaction failed. Two constructors are provided: one that takes a message and the deficiency amount, and another that takes a message, the deficiency amount, and an inner exception. The inner exception is useful for wrapping lower-level exceptions that caused the insufficient funds error, preserving the original error information.
using System;
public class InsufficientFundsException : Exception
{
public decimal Deficiency { get; private set; }
public InsufficientFundsException(string message, decimal deficiency) : base(message)
{
Deficiency = deficiency;
}
public InsufficientFundsException(string message, decimal deficiency, Exception innerException) : base(message, innerException)
{
Deficiency = deficiency;
}
}
Using the Custom Exception
This code demonstrates how to use the InsufficientFundsException
. A BankAccount
class is created with a Withdraw
method. If the withdrawal amount exceeds the account balance, an InsufficientFundsException
is thrown with a descriptive message and the deficiency amount. The Main
method then calls the Withdraw
method within a try-catch
block. The catch
block specifically handles the InsufficientFundsException
, printing the error message and the deficiency amount. A generic catch
block is also included to handle any other unexpected exceptions.
using System;
public class BankAccount
{
private decimal _balance;
public BankAccount(decimal initialBalance)
{
_balance = initialBalance;
}
public void Withdraw(decimal amount)
{
if (amount > _balance)
{
throw new InsufficientFundsException("Insufficient funds to complete the withdrawal.", amount - _balance);
}
_balance -= amount;
Console.WriteLine($"Withdrawal of {amount} successful. New balance: {_balance}");
}
public decimal GetBalance()
{
return _balance;
}
}
public class Example
{
public static void Main(string[] args)
{
BankAccount account = new BankAccount(100);
try
{
account.Withdraw(150);
}
catch (InsufficientFundsException ex)
{
Console.WriteLine($"Error: {ex.Message}");
Console.WriteLine($"Deficiency: {ex.Deficiency}");
// Log the exception, retry the transaction, or take other appropriate action.
}
catch (Exception ex)
{
Console.WriteLine($"An unexpected error occurred: {ex.Message}");
// Handle other exceptions
}
Console.WriteLine($"Current balance: {account.GetBalance()}");
}
}
Concepts Behind the Snippet
Custom exceptions allow developers to create specific exception types tailored to their application's needs. This makes exception handling more precise and informative. Key concepts include: Inheritance from the Exception
class, providing custom properties (like Deficiency
in this case) to hold relevant data, and using specific catch
blocks to handle different exception types appropriately. This increases code readability and maintainability.
Real-Life Use Case
In e-commerce applications, custom exceptions can be used to handle errors related to invalid payment methods, insufficient stock, or failed order processing. For instance, a PaymentFailedException
could be thrown if a payment fails, and a OutOfStockException
could be thrown if an item is no longer available. In API development, custom exceptions provide clear error responses to clients, making debugging easier and enhancing the API's usability.
Best Practices
catch
blocks. Only catch Exception
(or SystemException
) as a last resort to prevent hiding unexpected errors.
Interview Tip
During interviews, be prepared to discuss the benefits of custom exceptions over general exceptions. Highlight their role in providing more specific error information, improving code clarity, and enhancing error handling strategies. Explain how custom exceptions contribute to more robust and maintainable applications.
When to Use Them
Use custom exceptions when you need to represent errors that are specific to your application's domain. These errors should be distinct from standard .NET exceptions. For instance, if you're building a banking application, you might have exceptions for insufficient funds, invalid account numbers, or transaction failures. If your code throws many general Exception
s, consider creating custom exceptions to provide more context.
Memory Footprint
The memory footprint of a custom exception is similar to that of a standard .NET exception. Each exception object consumes memory to store its message, stack trace, and any custom properties. The memory overhead is typically small but can add up if exceptions are thrown frequently. Proper exception handling, such as avoiding unnecessary exceptions and handling them efficiently, can help minimize the memory footprint.
Alternatives
Alternatives to custom exceptions include using error codes, returning nullable values or using the TryXXX
pattern where a boolean is returned indicating success, and an out
parameter provides the result. However, custom exceptions offer a more structured and expressive way to handle errors, particularly for complex scenarios that require detailed error information.
Pros
Cons
FAQ
-
When should I inherit from
ApplicationException
instead ofException
?
In modern .NET development, inheriting fromApplicationException
is generally discouraged. It was originally intended for exceptions specific to an application, but it doesn't provide significant benefits over inheriting directly fromException
. Inheriting fromException
is the standard practice for creating custom exceptions. -
How do I handle exceptions in asynchronous methods?
In asynchronous methods, exceptions are propagated back to the calling thread when theTask
is awaited. You can usetry-catch
blocks around theawait
keyword to handle exceptions thrown by the asynchronous method. Unhandled exceptions in asynchronous methods will crash the application.