Bird
Raised Fist0
C Sharp (C#)programming~15 mins

Custom exception classes in C Sharp (C#) - Deep Dive

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Overview - Custom exception classes
What is it?
Custom exception classes are special error types you create yourself in C# to represent specific problems in your program. Instead of using general errors, you make your own named errors that explain exactly what went wrong. This helps your program handle mistakes clearly and safely. Custom exceptions are classes that inherit from the built-in Exception class.
Why it matters
Without custom exceptions, all errors look the same and it is hard to know what caused a problem or how to fix it. Custom exceptions let you give meaningful names and details to errors, making your program easier to debug and maintain. They also help other programmers understand your code better and handle errors properly.
Where it fits
Before learning custom exceptions, you should know basic C# classes and how to use try-catch blocks for error handling. After this, you can learn about advanced error handling patterns, logging errors, and designing robust applications that recover from failures.
Mental Model
Core Idea
A custom exception is a named error class you create to clearly represent a specific problem in your program.
Think of it like...
Imagine a toolbox where each tool has a special shape and name for a specific job. Custom exceptions are like custom-shaped tools that fit exactly the problem you want to fix, instead of using a generic tool for everything.
┌─────────────────────────────┐
│        Exception Base        │
│  (General error class)       │
└─────────────┬───────────────┘
              │
  ┌───────────┴────────────┐
  │ CustomExceptionClass    │
  │ (Specific error type)   │
  └────────────────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding Exception Basics
🤔
Concept: Learn what exceptions are and how C# uses the Exception class to represent errors.
In C#, an exception is an object that represents an error or unexpected event. The Exception class is the base for all errors. When something goes wrong, C# creates an Exception object and throws it. You can catch these exceptions using try-catch blocks to handle errors gracefully.
Result
You understand that exceptions are objects representing errors and that you can catch them to prevent crashes.
Knowing that exceptions are objects helps you see why you can create your own error types by making new classes.
2
FoundationUsing Try-Catch for Error Handling
🤔
Concept: Learn how to catch and handle exceptions using try-catch blocks.
A try block contains code that might cause an error. If an error happens, the catch block runs to handle it. For example: try { int x = int.Parse("abc"); // causes error } catch (Exception e) { Console.WriteLine("Error: " + e.Message); } This prevents the program from crashing and shows a message instead.
Result
You can catch errors and respond to them instead of letting the program crash.
Handling exceptions lets your program recover or fail gracefully, improving user experience.
3
IntermediateCreating a Simple Custom Exception
🤔Before reading on: do you think a custom exception must add new code, or can it be empty and still work? Commit to your answer.
Concept: Learn how to make a new exception class by inheriting from Exception.
You create a custom exception by making a class that extends Exception. It can be empty or add extra details. Example: public class MyCustomException : Exception { public MyCustomException(string message) : base(message) {} } Now you can throw new MyCustomException("Something went wrong") and catch it specifically.
Result
You can define your own error types to represent specific problems.
Understanding inheritance lets you create meaningful error types that fit your program's needs.
4
IntermediateThrowing and Catching Custom Exceptions
🤔Before reading on: do you think catching a custom exception requires a special catch block or can a general Exception catch it? Commit to your answer.
Concept: Learn how to throw your custom exception and catch it specifically or generally.
You throw a custom exception like this: throw new MyCustomException("Bad data"); You can catch it specifically: try { // code } catch (MyCustomException e) { Console.WriteLine("Custom error: " + e.Message); } catch (Exception e) { Console.WriteLine("Other error: " + e.Message); } Specific catch blocks run before general ones.
Result
You can handle specific errors differently from general errors.
Knowing catch order helps you write precise error handling that matches your program's logic.
5
IntermediateAdding Extra Data to Custom Exceptions
🤔
Concept: Learn how to add properties to your custom exception to carry more error details.
You can add fields or properties to your custom exception class to store extra info: public class MyCustomException : Exception { public int ErrorCode { get; } public MyCustomException(string message, int code) : base(message) { ErrorCode = code; } } This helps the catch block know more about the error.
Result
Your custom exceptions can carry detailed information for better error handling.
Adding data to exceptions makes error handling smarter and debugging easier.
6
AdvancedImplementing Serialization in Custom Exceptions
🤔Before reading on: do you think custom exceptions need special code to work across app domains or save/load? Commit to your answer.
Concept: Learn why and how to make your custom exceptions support serialization for advanced scenarios.
Serialization lets exceptions be saved and restored, for example when sending errors over networks or between app parts. To support this, implement a special constructor and mark the class as [Serializable]: [Serializable] public class MyCustomException : Exception { public MyCustomException(string message) : base(message) {} protected MyCustomException(SerializationInfo info, StreamingContext context) : base(info, context) {} } This is needed for some frameworks and remoting.
Result
Your custom exceptions can be serialized and used in advanced scenarios safely.
Understanding serialization prepares you for building robust distributed applications.
7
ExpertBest Practices and Performance Considerations
🤔Before reading on: do you think creating many exceptions in normal code is efficient or costly? Commit to your answer.
Concept: Learn when and how to use custom exceptions wisely to avoid performance issues and maintain clarity.
Throwing exceptions is slow compared to normal code. Use exceptions only for truly unexpected errors, not for regular control flow. Also, keep exception classes simple and meaningful. Avoid catching exceptions you can't handle. Document your custom exceptions so others know when they occur. Example: Don't throw exceptions inside loops for normal checks; use if-else instead.
Result
You write efficient, clear, and maintainable error handling code.
Knowing the cost and purpose of exceptions helps you design better, faster programs.
Under the Hood
When you throw an exception, C# creates an Exception object and looks up the call stack for a matching catch block. This unwinding process stops at the first suitable catch. Custom exceptions are just classes derived from Exception, so they behave the same way but carry specific type information. The runtime uses the exception type to match catch blocks.
Why designed this way?
The Exception class hierarchy allows programmers to create specific error types without changing the runtime. This design supports polymorphism, letting catch blocks handle broad or narrow error categories. Serialization support was added for remoting and cross-process error handling, reflecting the needs of distributed applications.
┌───────────────┐
│ Throw Exception│
└───────┬───────┘
        │
        ▼
┌───────────────────────────┐
│ Exception Object Created   │
│ (Custom or Built-in)       │
└─────────────┬─────────────┘
              │
              ▼
┌───────────────────────────┐
│ Stack Unwinding Begins     │
│ Searching for Catch Block │
└─────────────┬─────────────┘
              │
      ┌───────┴────────┐
      │ Match Found?   │
      └───────┬────────┘
              │Yes
              ▼
┌───────────────────────────┐
│ Catch Block Executes       │
└───────────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Can you catch a custom exception using a general Exception catch block? Commit to yes or no.
Common Belief:You must always catch custom exceptions with their exact type; general Exception catch blocks won't catch them.
Tap to reveal reality
Reality:Custom exceptions inherit from Exception, so a general catch(Exception) block will catch all exceptions, including custom ones.
Why it matters:Believing otherwise can lead to overly complex catch blocks or missed error handling opportunities.
Quick: Do you think creating many custom exceptions for every small error is good practice? Commit to yes or no.
Common Belief:More custom exceptions always make error handling clearer and better.
Tap to reveal reality
Reality:Too many custom exceptions can clutter code and confuse users; it's better to create them only for meaningful, distinct error cases.
Why it matters:Overusing custom exceptions makes maintenance harder and can reduce code readability.
Quick: Do you think throwing exceptions is a cheap operation suitable for normal program flow? Commit to yes or no.
Common Belief:Throwing exceptions is fast and can be used like normal conditional checks.
Tap to reveal reality
Reality:Throwing exceptions is costly in performance and should be reserved for unexpected errors, not regular control flow.
Why it matters:Misusing exceptions can slow down your program and make it harder to debug.
Quick: Can custom exceptions be serialized automatically without extra code? Commit to yes or no.
Common Belief:Custom exceptions work with serialization out of the box without any special code.
Tap to reveal reality
Reality:To support serialization, custom exceptions must implement special constructors and be marked [Serializable]. Without this, serialization can fail.
Why it matters:Ignoring this causes runtime errors in distributed or remoting scenarios.
Expert Zone
1
Custom exceptions should be immutable after creation to avoid confusing error states during handling.
2
Including meaningful error codes or enums in custom exceptions helps integrate with logging and monitoring systems.
3
Overriding the ToString() method in custom exceptions can provide richer debugging information without extra logging.
When NOT to use
Avoid custom exceptions for simple validation errors where returning error codes or using built-in exceptions is sufficient. For performance-critical code, prefer error codes or result objects instead of exceptions.
Production Patterns
In real-world systems, custom exceptions are used to represent domain-specific errors like 'UserNotFoundException' or 'PaymentFailedException'. They are often combined with centralized logging, error codes, and user-friendly messages. Exception filters and middleware catch these exceptions to provide consistent error responses.
Connections
Polymorphism
Custom exceptions use inheritance, a core part of polymorphism in object-oriented programming.
Understanding polymorphism helps you see how catch blocks can handle different exception types flexibly.
Error Codes in Systems Programming
Custom exceptions are an object-oriented alternative to error codes used in lower-level programming.
Knowing error codes helps appreciate why exceptions improve clarity and reduce error-handling boilerplate.
Medical Diagnosis
Just like doctors classify diseases into specific types for targeted treatment, custom exceptions classify errors for precise handling.
This connection shows how classification improves problem-solving in both programming and healthcare.
Common Pitfalls
#1Creating a custom exception without calling the base Exception constructor.
Wrong approach:public class MyException : Exception { public MyException(string message) { // missing base(message) } }
Correct approach:public class MyException : Exception { public MyException(string message) : base(message) {} }
Root cause:Forgetting to pass the message to the base class means the exception message is empty, losing important error info.
#2Catching Exception before custom exceptions, causing custom catch blocks to never run.
Wrong approach:try { // code } catch (Exception e) { // general catch } catch (MyException e) { // custom catch }
Correct approach:try { // code } catch (MyException e) { // custom catch } catch (Exception e) { // general catch }
Root cause:Catch blocks are checked top to bottom; placing general catch first swallows all exceptions, blocking specific handlers.
#3Using exceptions for normal control flow like checking if a file exists.
Wrong approach:try { var content = File.ReadAllText(path); } catch (FileNotFoundException) { // handle missing file }
Correct approach:if (File.Exists(path)) { var content = File.ReadAllText(path); } else { // handle missing file }
Root cause:Exceptions are expensive; using them for expected conditions hurts performance and code clarity.
Key Takeaways
Custom exception classes let you create meaningful, specific error types that improve program clarity and error handling.
They inherit from the base Exception class, so they integrate seamlessly with C#'s try-catch system.
Adding extra data and supporting serialization makes custom exceptions powerful for real-world applications.
Use exceptions only for unexpected errors, not normal program flow, to keep your code efficient and readable.
Proper ordering of catch blocks and calling base constructors are essential to avoid common bugs.

Practice

(1/5)
1. What is the main reason to create a custom exception class in C#?
easy
A. To automatically fix errors when they occur
B. To make the program run faster
C. To avoid using try-catch blocks
D. To represent specific error conditions clearly in your program

Solution

  1. Step 1: Understand the purpose of exceptions

    Exceptions represent errors or unexpected situations in a program.
  2. Step 2: Identify why custom exceptions are used

    Custom exceptions help describe specific problems clearly, making error handling easier and more meaningful.
  3. Final Answer:

    To represent specific error conditions clearly in your program -> Option D
  4. Quick Check:

    Custom exceptions clarify errors = A [OK]
Hint: Custom exceptions explain specific errors clearly [OK]
Common Mistakes:
  • Thinking custom exceptions improve speed
  • Believing they remove need for try-catch
  • Assuming they fix errors automatically
2. Which of the following is the correct way to declare a custom exception class named MyException in C#?
easy
A. class MyException : Exception { public MyException(string message) : base(message) {} }
B. class MyException { public MyException(string message) {} }
C. class MyException : int { public MyException(string message) : base(message) {} }
D. class MyException : Exception { public void MyException(string message) {} }

Solution

  1. Step 1: Check inheritance from Exception

    Custom exceptions must inherit from Exception to behave like exceptions.
  2. Step 2: Verify constructor calls base constructor

    The constructor should call base(message) to pass the error message properly.
  3. Final Answer:

    class MyException : Exception { public MyException(string message) : base(message) {} } -> Option A
  4. Quick Check:

    Inherit Exception + call base constructor = A [OK]
Hint: Inherit Exception and call base constructor for message [OK]
Common Mistakes:
  • Not inheriting from Exception
  • Using wrong base class like int
  • Defining constructor as void method
3. What will be the output of the following C# code?
class MyException : Exception { public MyException(string message) : base(message) {} }
try {
throw new MyException("Error happened");
} catch (MyException ex) {
Console.WriteLine(ex.Message);
}
medium
A. Exception caught
B. Error happened
C. MyException
D. No output

Solution

  1. Step 1: Understand the throw statement

    The code throws a MyException with message "Error happened".
  2. Step 2: Catch block prints exception message

    The catch block catches MyException and prints ex.Message, which is "Error happened".
  3. Final Answer:

    Error happened -> Option B
  4. Quick Check:

    Throw and catch prints message = C [OK]
Hint: Catch prints exception message property [OK]
Common Mistakes:
  • Expecting class name instead of message
  • Thinking catch block won't run
  • Assuming no output from exception
4. Identify the error in this custom exception class declaration:
class MyError : Exception {
public MyError(string msg) {
base(msg);
}
}
medium
A. The class must not inherit from Exception
B. The constructor should be named differently from the class
C. The constructor should call base(msg) using a colon, not inside the body
D. The base class Exception does not accept a string parameter

Solution

  1. Step 1: Check constructor syntax for base call

    In C#, calling the base class constructor must be done with a colon after the constructor signature, not inside the body.
  2. Step 2: Identify correct syntax

    The correct syntax is public MyError(string msg) : base(msg) {}, not calling base(msg); inside the constructor body.
  3. Final Answer:

    The constructor should call base(msg) using a colon, not inside the body -> Option C
  4. Quick Check:

    Base constructor call uses colon syntax = B [OK]
Hint: Call base constructor with colon, not inside method body [OK]
Common Mistakes:
  • Calling base constructor inside body instead of colon
  • Not inheriting from Exception
  • Misnaming constructor
5. You want to create a custom exception InvalidAgeException that should be thrown when a user's age is less than 0 or greater than 120. Which of the following code snippets correctly defines and uses this exception?
hard
A. class InvalidAgeException : Exception { public InvalidAgeException(string msg) : base(msg) {} }
void CheckAge(int age) { if(age < 0 || age > 120) throw new InvalidAgeException("Age is invalid"); }
B. class InvalidAgeException { public InvalidAgeException(string msg) {} }
void CheckAge(int age) { if(age < 0 || age > 120) throw new InvalidAgeException("Age is invalid"); }
C. class InvalidAgeException : Exception { public void InvalidAgeException(string msg) {} }
void CheckAge(int age) { if(age < 0 || age > 120) throw new InvalidAgeException("Age is invalid"); }
D. class InvalidAgeException : Exception { public InvalidAgeException() {} }
void CheckAge(int age) { if(age < 0 || age > 120) throw new InvalidAgeException(); }

Solution

  1. Step 1: Verify custom exception class definition

    The class inherits from Exception and has a constructor calling base(msg) to pass the message.
  2. Step 2: Check usage in method

    The method throws the exception with a message when age is invalid, which matches the requirement.
  3. Final Answer:

    Correct class inheritance, constructor, and usage with message -> Option A
  4. Quick Check:

    Inherit Exception + throw with message = D [OK]
Hint: Inherit Exception, add constructor, throw with message [OK]
Common Mistakes:
  • Not inheriting from Exception
  • Defining constructor as void method
  • Missing message in exception constructor