BahirDar Institute of Technology
Faculty of Computing (Information Technology)
Advanced Database(G6 project)
Students Name ld
1.KALKIDAN TILAHUN ............ 1506668
2.TESFALEM TEKLEMARIAM ..... 1507932
3. NATNAEL TEMESGEN ............ 1512556
4.MEWUDED TESFAHUNEGN........1507141
5.TSION TESFAYE ........................1508052
6.YONAS TESHOME.................. 1508367
7. HAYMANOT WORKU............... 1506489
8.DESKIOUS YISMAW................ 1505724
9.YORDANOS YITBAREK............. 1508409
10.BELAY ZELEK......................... 1505278
submission date:
submitted to:
1
Table of Contents
page
Exception handling in c#.net...........................................................................3
2
introduction..................................................................................................3
1. Introduction to Exception Handling in C#....................................................5
1.1 What is an Exception?........................................................................5
1.2 Importance of Exception Handling......................................................5
1.3 Errors vs. Exceptions in C#................................................................6
2.Exception Handling Mechanisms in C#.....................................................7
2.1 The try, catch, and finally Blocks.........................................................8
2.2 Re-throwing Exceptions:
…………………………………………………………………12
2.3 Re-throwing Exceptions (throw; vs. throw ex;)..................................12
3.Exception Types in C#.............................................................................13
Built-in Exceptions (System.Exception and Derived Classes)..................13
Common Runtime Exceptions (DivideByZeroException,
NullReferenceException, etc.).................................................................15
3. NullReferenceException (Accessing Null Object).................................16
User-defined (Custom) Exceptions..........................................................20
4. Exception Hierarchy in C#...................................................................21
Checked vs. Unchecked Exceptions.........................................................27
4.3 Exception Inheritance Structure.......................................................29
5. Handling Multiple Exceptions..................................................................29
Using Multiple catch Blocks in C#...........................................................29
Filtering Exceptions Using catch when in C#...........................................32
. Basic Nested Try-Catch Block................................................................33
6. Creating and Using Custom Exceptions..................................................36
Definition of a Custom Exception Class in C#.........................................36
Implementation of Custom Exception Handling in C#.............................36
Adding Custom Properties and Methods to a Custom Exception in C#. . .37
7. Exception Propagation and Best Practices..............................................39
Exception Propagation Across Methods...................................................39
Best Practices in Exception Handling......................................................40
Writing Meaningful Error Messages.........................................................40
3
Avoiding Overuse of Try-Catch Blocks.....................................................41
8. Exception Handling in Delegates and Events.........................................41
Managing Exceptions in Event-Driven Programming...............................41
Exception Handling in Callback Functions...............................................42
9. Global Exception Handling in C#............................................................43
Using AppDomain.UnhandledException in C#.........................................43
Implementing Global Exception Handling in Web Applications (ASP.NET)
................................................................................................................44
10. Debugging and Error Recovery Strategies............................................45
Using Debuggers (Visual Studio Exception Settings)...............................45
Graceful Degradation and Recovery........................................................45
11 Summery..............................................................................................46
12 References.............................................................................................47
4
Exception handling in c#.net
introduction
Exception handling in C# is a structured approach to managing runtime
errors and ensuring that applications continue functioning smoothly under
unexpected conditions. By using try, catch, finally, and throw blocks,
developers can anticipate potential failures, respond appropriately, and
prevent abrupt crashes.
Exceptions in C# arise due to various issues, such as invalid input, resource
unavailability, or logical mistakes. Instead of terminating a program
immediately, exception handling allows developers to define specific
responses for different error scenarios, making applications more reliable and
user-friendly.
5
By leveraging built-in exception types, creating custom exceptions, and
following best practices such as meaningful error logging and avoiding
generic exception catching, developers can build robust software that adapts
to varying runtime challenges. Effective exception handling not only
enhances maintainability but also contributes to an improved user
experience by providing informative error messages and graceful recovery
mechanisms.
6
1. Introduction to Exception Handling in C#
What is an Exception?
Importance of Exception Handling
Errors vs. Exceptions in C#
1.1 What is an Exception?
📌 An exception is an error that occurs during program execution,
disrupting normal flow.
📌 Exceptions arise due to unexpected conditions (e.g., invalid input,
division by zero, null references).
📌 C# provides a structured exception-handling mechanism using
try, catch, finally, and throw.
📌 Handling exceptions ensures that the program can continue
running despite errors.
Example:
csharp
int number = int.Parse("abc"); // Causes FormatException
Exceptions must be handled properly to prevent crashes.
1.2 Importance of Exception Handling
✅ Prevents program crashes by gracefully managing errors.
✅ Improves code reliability and maintainability.
✅ Enhances the user experience by preventing unexpected failures.
✅ Helps developers debug errors efficiently by logging detailed
exception data.
7
✅ Ensures critical resources (e.g., files, database connections) are
properly released.
Example using try-catch
try
int number = int.Parse("abc"); // FormatException occurs
catch (FormatException ex)
Console.WriteLine("Handled Exception: Invalid format.");
Exception handling prevents runtime failures and ensures stable
execution.
1.3 Errors vs. Exceptions in C#
Errors
Errors are unexpected issues that occur at a system level and often
indicate serious problems, such as:
Syntax errors: Issues in the code that prevent compilation
(e.g., missing
semicolons, typos).
Logical errors: Bugs in the program that lead to incorrect
results.
Runtime errors: Problems like insufficient memory or
hardware failures.
Errors generally cannot be handled by the program itself. They often
require debugging and fixes in the code before execution.
Exceptions
8
Exceptions are runtime issues that disrupt the normal flow of a
program but can be handled gracefully using a try-catch block.
Common exceptions include:
DivideByZeroException: Occurs when dividing by zero.
NullReferenceException: Happens when accessing an
object reference that is null.
IndexOutOfRangeException: Triggered when accessing an
invalid array index.
C# provides a structured way to handle exceptions using try, catch, and
finally:
try
{
int result = 10 / 0; // This will cause DivideByZeroException
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Cannot divide by zero!");
}
finally
{
Console.WriteLine("Execution completed.");
}
Exceptions are part of the System.Exception class and can be caught,
logged, or even rethrown for further handling.
Key Differences
Aspect Errors Exceptions
Occurrenc Compile-time or
Only at runtime
e runtime
Recoverabl Yes, if handled
Usually no
e? properly
Managed with try-
Handling Requires fixing code
catch
2.Exception Handling Mechanisms in C#
The try, catch, and finally Blocks
9
Throwing Exceptions (throw Keyword)
Re-throwing Exceptions (throw vs. throw ex)
Exception handling in C# is a mechanism that allows a program to detect,
handle, and recover from runtime errors gracefully, instead of crashing
unexpectedly.
2.1 The try, catch, and finally Blocks
Key Components of Exception Handling
1. Try Block: This contains the code that may cause an exception.
2. Catch Block: This handles exceptions when they occur.
3. Finally Block: This executes code regardless of whether an exception
occurs.
4. Throw Statement: This generates an exception manually.
2.1.1 The try Block
The try block contains the code that might throw an exception. It ensures that risky
operations (such as division by zero or accessing null references) are caught.
Example:
try
{
int num = 10;
int result = num / 0; // Exception occurs here
Console.WriteLine(result);
}
If an exception occurs inside the try block, control immediately passes to the catch
block.
2.1.2 The catch Block
The catch block handles the exception and prevents the program from crashing. You can
define multiple catch blocks to handle different exception types separately.
Example:
try
{
int[] numbers = {1, 2, 3};
Console.WriteLine(numbers[5]); // IndexOutOfRangeException
10
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine($"Exception caught: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine("An unexpected error occurred.");
}
📌 Best Practice: Catch specific exceptions first before using a generic catch (Exception
ex) block.
2.1.3 The finally Block
The finally block always executes whether an exception occurs or not. It's commonly
used for cleanup operations like closing files or database connections.
The throwstatement
– Allows a user/programmer to throw an exception object
• E.g.:
throw new Exception("Exception in Throw-Test Method" );
– string passed to exception object’s constructor becomes exception object’s error
message
– Can throw only an object of class Exception or one of its derived classes
– You can customize the exception type thrown from methods
// Using finally blocks.
using System;
// demonstrating that ‘finally’ always executes
class UsingExceptions
{
entry point for application
static void Main( string[] args )
{
// Case 1: No exceptions occur in called method.
11
Console.WriteLine( "Calling DoesNotThrowException" );
DoesNotThrowException();
// Case 2: Exception occurs in called method and is caught
// in called method.
Console.WriteLine( "\nCalling ThrowExceptionWithCatch" );
Console.WriteLine( "\nCalling ThrowExceptionWithCatch" );
ThrowExceptionWithCatch();
// Case 3: Exception occurs in called method, but not caught
// in called method, because no catch handlers in called method.
Console.WriteLine( 24 "\nCalling ThrowExceptionWithoutCatch" );
// call ThrowExceptionWithoutCatch
try
{
ThrowExceptionWithoutCatch();
}
// process exception returned from
// ThrowExceptionWithoutCatch
catch
{
Console.WriteLine( "Caught exception from " + 37 "ThrowExceptionWithoutCatch in
Main" );
}
// Case 4: Exception occurs in called method and is caught
// in called method, then rethrown to caller.
Console.WriteLine( 43 "\nCalling ThrowExceptionCatchRethrow" );
// call ThrowExceptionCatchRethrow
try
12
{
ThrowExceptionCatchRethrow();
}
}
// process exception returned from
// ThrowExceptionCatchRethrow
catch
{
Console.WriteLine( "Caught exception from " + "ThrowExceptionCatchRethrow in
Main" );
}
} // end method Main
2.1.4 Throw block
In C#, the throw keyword is used to raise exceptions within a program. A
throw block typically appears inside a try-catch statement or a method,
allowing developers to explicitly signal that an error has occurred.
Key Points:
Usage: The throw statement is used to create exceptions and
terminate the current flow of execution.
Syntax: It is often used inside a try block or when handling exceptions
in a catch block.
Example:
try
{
int result = 10 / 0; // This will cause a divide-by-zero
exception
}
catch (Exception ex)
{
13
throw new InvalidOperationException("An error occurred",
ex);
}
2.2 Re-throwing Exceptions: If used inside a catch block without
arguments (throw;), it preserves the original stack trace.
2.3 Re-throwing Exceptions (throw; vs. throw ex;)
1. throw; preserves the original stack trace
o When an exception is caught and re-thrown using throw;, the
stack trace remains unchanged.
o This is useful for debugging because the original error location is
visible.
2. throw ex; resets the stack trace
o Using throw ex; within a catch block makes it appear as if the
exception originated at the throw ex; statement.
o This can obscure the actual source of the error, making
debugging more difficult.
3. Best Practice: Always use throw; unless modifying the
exception
o If the goal is to log the error and re-throw it, throw; is the
recommended approach.
o If you need to modify the exception (e.g., changing the error
message), use throw new Exception("New message", ex);.
4. Example demonstrating the difference
try
{
int result = 10 / 0; // Causes a divide-by-zero exception
}
catch (Exception ex)
{
Console.WriteLine("Error caught!");
// Wrong approach: resets the stack trace
throw ex;
// Correct approach: maintains original error details
throw;
}
5. Impact on debugging
14
o throw; helps developers track down the exact location of the
exception.
o throw ex; can lead to misleading stack traces, making
troubleshooting harder.
3.Exception Types in C#
Built-in Exceptions (System.Exception and Derived Classes)
Common Runtime Exceptions (DivideByZeroException,
NullReferenceException, etc.)
User-defined (Custom) Exceptions
3.1 Built-in Exceptions (System.Exception and Derived Classes)
3.1.1. Overview of Built-in Exceptions in C#
C# provides a robust exception handling system through built-in
exception classes.
All exceptions are derived from the System.Exception base class.
These built-in exceptions handle various runtime errors such as
division by zero, invalid operations, file access errors, and type
mismatches.
3.1.2. System.Exception (Base Class)
System.Exception is the base class for all exceptions in C#.
It provides fundamental properties like:
o Message → Describes the error.
o StackTrace → Shows where the error occurred.
o InnerException → Stores nested exceptions if one caused
another.
✅ Example: Throwing a Base Exception
throw new Exception("An error occurred in the application.");
This is the generic exception type, but specific exceptions are
preferred for better error handling.
3.1.3. Common Derived Exception Classes
15
a) System.SystemException
The base class for system-level exceptions (e.g., arithmetic errors,
null references).
b) System.ApplicationException
Used for application-defined exceptions (but rarely used today).
c) System.NullReferenceException
Occurs when trying to access a member of an object that is null.
Example: Calling a method on an uninitialized object reference.
🚫 Example: Causes a Null Reference Exception
csharp
string text = null;
Console.WriteLine(text.Length); // Error: NullReferenceException
3.1.4. Specific Built-in Exceptions
a) Arithmetic Exceptions
System.DivideByZeroException → Raised when dividing by zero.
System.OverflowException → Occurs when a numeric operation
exceeds limits.
b) Type-Related Exceptions
System.InvalidCastException → Thrown when a failed type
conversion occurs.
System.FormatException → Raised for invalid data formats (e.g.,
incorrect parsing).
c) File & IO Exceptions
System.IO.FileNotFoundException → Raised when a file is missing.
System.IO.IOException → Generic exception for file-handling errors.
d) Argument Exceptions
System.ArgumentException → Thrown for invalid arguments passed
to a method.
16
System.ArgumentNullException → Raised when a null argument is
unexpectedly used.
3.1.5. Exception Handling Best Practices
✅ Always use specific exceptions instead of the generic Exception
class.
🔍 Log exceptions using ex.Message and ex.StackTrace for debugging.
🚀 Use try-catch-finally to ensure graceful error handling.
✅ Example: Handling a Specific Exception
try
{
int result = 10 / 0;
}
catch (DivideByZeroException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
This ensures that only DivideByZeroException is handled properly.
3.1.6. : Common Built-in Exceptions
Exception Class Description Example
NullReferenceExcept Occurs when accessing string text = null;
ion null text.Length;
DivideByZeroExcepti Raised when dividing by
int result = 10 / 0;
on zero
InvalidCastException Failed type conversion (int)obj;
FileNotFoundExcepti
Missing file error File.ReadAllText("missing.txt
on
");
3.2 Common Runtime Exceptions (DivideByZeroException,
NullReferenceException, etc.)
17
3.2.1. Overview of Common Runtime Exceptions
Runtime exceptions occur while the program is running due to
logical errors, invalid operations, or unexpected conditions.
These exceptions are automatically thrown by the CLR (Common
Language Runtime) when a program encounters issues.
Proper exception handling using try-catch blocks can prevent
application crashes and improve stability.
3.2.2. DivideByZeroException (Arithmetic Error)
Occurs when a program attempts to divide a number by zero.
This is an arithmetic exception automatically raised by the CLR.
🚫 Example: Causes DivideByZeroException
csharp
int result = 10 / 0; // ERROR: Division by zero is not allowed
The above code will fail because division by zero is undefined.
✅ Handling the Exception
csharp
try
{
int result = 10 / 0; // Causes exception
}
catch (DivideByZeroException ex)
{
Console.WriteLine($"Error: {ex.Message}"); // Displays error
message
}
A try-catch block ensures that the program does not crash
unexpectedly.
3.2.1. NullReferenceException (Accessing Null Object)
Occurs when attempting to access a member of an object that
is null.
This happens when a variable is declared but not initialized before
use.
🚫 Example: Causes NullReferenceException
csharp
string text = null;
Console.WriteLine(text.Length); // ERROR: Object reference not set to
an instance
Since text is null, trying to access Length causes the exception.
✅ Handling the Exception
18
csharp
string text = null;
try
{
Console.WriteLine(text.Length); // Causes exception
}
catch (NullReferenceException ex)
{
Console.WriteLine("Error: The object is null.");
}
Always check if an object is null before accessing its properties.
3.2.2. IndexOutOfRangeException (Invalid Array Index)
Occurs when trying to access an array index that does not
exist.
Arrays in C# are zero-based, meaning the first index is 0.
🚫 Example: Causes IndexOutOfRangeException
csharp
int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[5]); // ERROR: Index 5 is out of bounds
There is no index 5, leading to an exception.
✅ Handling the Exception
csharp
int[] numbers = { 1, 2, 3 };
try
{
Console.WriteLine(numbers[5]); // Causes exception
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("Error: Invalid array index.");
}
Always validate indices before accessing an array element.
3.2.3. InvalidCastException (Type Conversion Error)
Occurs when attempting an illegal type conversion.
The conversion fails if the types are incompatible.
🚫 Example: Causes InvalidCastException
csharp
19
object obj = "Hello";
int num = (int)obj; // ERROR: Cannot convert a string to an integer
Since "Hello" is a string, it cannot be cast to an int.
✅ Handling the Exception
csharp
object obj = "Hello";
try
{
int num = (int)obj; // Causes exception
}
catch (InvalidCastException ex)
{
Console.WriteLine("Error: Type conversion is invalid.");
}
Use safe conversion methods like Convert.ToInt32() or int.TryParse().
3.2.4. FormatException (Invalid Data Format)
Occurs when parsing data that does not match the expected
format.
Common in string-to-number conversions.
🚫 Example: Causes FormatException
csharp
string input = "abc";
int number = int.Parse(input); // ERROR: "abc" is not a valid integer
"abc" cannot be converted to an integer, leading to an exception.
✅ Handling the Exception
csharp
string input = "abc";
try
{
int number = int.Parse(input); // Causes exception
}
catch (FormatException ex)
{
Console.WriteLine("Error: Invalid format.");
}
Always validate user input before parsing data.
20
3.2.5. FileNotFoundException (Missing File Error)
Occurs when trying to access a file that does not exist.
Raised when using file handling methods like File.ReadAllText().
🚫 Example: Causes FileNotFoundException
csharp
string content = File.ReadAllText("nonexistent.txt"); // ERROR: File not
found
If "nonexistent.txt" does not exist, the exception occurs.
✅ Handling the Exception
csharp
try
{
string content = File.ReadAllText("nonexistent.txt"); // Causes
exception
}
catch (FileNotFoundException ex)
{
Console.WriteLine("Error: The file does not exist.");
}
Always check if the file exists before attempting to read it.
3.2.6. Summary Table: Common Runtime Exceptions
Exception Class Cause Example
Division
DivideByZeroException int result = 10 / 0;
by zero
Accessin
NullReferenceExceptio string text = null;
g a null
n text.Length;
object
Using an
IndexOutOfRangeExce invalid array[5] on a 3-element
ption array array
index
Illegal
type (int)obj; when obj is a
InvalidCastException
conversi string
on
FormatException Parsing int.Parse("abc");
21
Exception Class Cause Example
invalid
data
format
Trying to
read a
non-
File.ReadAllText("missing.
FileNotFoundException existent
txt");
file
3.2.7. Best Practices for Handling Runtime Exceptions
✅ Always use specific exceptions (e.g., DivideByZeroException)
instead of a generic Exception.
🔍 Log error details using ex.Message for debugging purposes.
🔹 Perform input validation before calling conversion methods like
Parse().
🚀 Use try-catch-finally blocks to ensure graceful error handling.
🏆 Use custom exceptions for application-specific errors when
necessary
3.3 User-defined (Custom) Exceptions
User-Defined Exception Basics
🔹 Custom exceptions help identify specific error scenarios in
an application.
🔹 They make debugging easier by providing meaningful error
messages.
🔹 They improve code organization by categorizing errors.
3.3.1 Creating a Custom Exception
🟢 Inherit from Exception (or a subclass like
ApplicationException).
🟢 Define constructors to initialize the exception message.
22
🟢 Optionally, add properties or methods for additional
functionality.
public class CustomException : Exception
{
public CustomException(string message) : base(message)
{}
}
3.3.2 Throwing a Custom Exception
✅ Use the throw keyword inside your code.
✅ Provide a descriptive message to clarify the error.
csharp
throw new CustomException("Custom error occurred!");
Handling a Custom Exception
📌 Use try-catch blocks for safe exception handling.
📌 Catch the exception and perform appropriate actions.
try
{
throw new CustomException("Something went wrong.");
}
catch (CustomException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
3.3.3 Best Practices for Custom Exceptions
⚡ Be specific—name exceptions clearly (e.g.,
InvalidInputException).
⚡ Keep it lightweight—avoid unnecessary code.
⚡ Use logging—record exceptions for debugging.
4. Exception Hierarchy in C#
23
Base Class (System.Exception)
Checked vs. Unchecked Exceptions
Exception Inheritance Structure
4.1 Base Class (System.Exception)
Basic Class Exception (System.Exception) in C#
📌 System.Exception is the base class for all exceptions in C#.
📌 It provides a foundation for handling and defining errors.
📌 Every built-in and custom exception inherits from System.Exception.
C# has a large set of built-in exceptions, organized under the System.Exception
hierarchy. Exceptions are classified based on their usage and severity.
Below is a comprehensive list of all major exceptions in C#:
🔹 4.1.1 Root Exception Class
Exception → The base class for all exceptions.
🔹 4.1.2. System-Level Exceptions (SystemException)
SystemException → Base class for system-related exceptions.
🟢 Common System Exceptions:
NullReferenceException → Using an object reference that is null.
sing System;
class Program
{
static void Main()
{
try
{
string text = null;
Console.WriteLine(text.Length); // ❌ NullReferenceException
}
catch (NullReferenceException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
24
}
}
🔹 Explanation text is null, so calling .Length on it triggers a NullReferenceException.
IndexOutOfRangeException → Trying to access an array outside of its bounds.
InvalidOperationException → When an operation is invalid in the current state.
StackOverflowException → Infinite recursion causes a stack overflow.
OutOfMemoryException → When the system runs out of memory.
AccessViolationException → Trying to access protected memory.
FormatException → Invalid format for data conversion (e.g., parsing a string as an
integer).
TypeInitializationException → Error occurs when initializing a static type.
NotImplementedException → When a method is declared but not implemented.
NotSupportedException → When an operation is unsupported.
🔹4.1. 3. Argument Exceptions (ArgumentException)
ArgumentException → Base class for argument-related exceptions.
🟢 Specific Argument Exceptions:
ArgumentNullException → A method argument is null but shouldn't be.
ArgumentOutOfRangeException → A parameter is outside the allowed range.
🔹4.1. 4. Arithmetic and Mathematical Exceptions (ArithmeticException)
ArithmeticException → Base class for math-related exceptions.
🟢 Specific Arithmetic Exceptions:
DivideByZeroException → Division by zero
Occurs when dividing a number by zero.
Example
csharp
using System;
class Program
{
static void Main()
{
try
{
25
int result = 10 / 0; // ❌ DivideByZeroException
}
catch (DivideByZeroException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
🔹 Explanation Division by zero is not allowed, so the exception is triggered.
OverflowException → Numeric operation exceeded the allowed range.
🔹4.1. 5. Input/Output Exceptions (IOException)
IOException → Base class for input/output errors.
🟢 Specific IO Exceptions:
FileNotFoundException → File not found.
DirectoryNotFoundException → Directory not found.
PathTooLongException → File path exceeds the maximum allowed length.
UnauthorizedAccessException → No permission to access a file.
EndOfStreamException → End of a data stream is unexpectedly reached.
DriveNotFoundException → The specified drive does not exist.
🔹4.1. 6. Reflection and Type Exceptions
TypeLoadException → Error loading a type.
MissingMethodException → A method was expected but doesn't exist.
MissingMemberException → A class member was expected but doesn't exist.
TargetInvocationException → When a method invocation fails in reflection.
🔹4.1. 7. Security Exceptions
SecurityException → Violations of security permissions.
🔹 4.1.8. Threading and Concurrency Exceptions
ThreadAbortException → When a thread is aborted.
ThreadInterruptedException → When a thread is interrupted.
SynchronizationLockException → When synchronization is incorrectly used.
TimeoutException → When an operation takes longer than expected.
26
🔹4.1. 9. Database-Related Exceptions
DbException → Base class for ADO.NET database exceptions.
SqlException → Issues related to SQL Server queries.
Occurs during SQL operations with incorrect queries or connection issues.
Example
using System;
using System.Data.SqlClient;
class Program
{
static void Main()
{
try
{
string connectionString = "InvalidConnectionString";
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open(); // ❌ SqlException
}
}
catch (SqlException ex)
{
Console.WriteLine($"Database Error: {ex.Message}");
}
} 🔹 Explanation Trying to open an invalid database connection causes an
exception.
DataException → General data-access-related error.
ConstraintException → When a database constraint fails.
InvalidConstraintException → When a database constraint is violated.
🔹4.1. 10. Serialization Exceptions
SerializationException → Errors occurring during serialization.
🔹4.1. 11. Networking Exceptions
WebException → Network error (e.g., failed HTTP request).
27
SocketException → Error in network socket communication.
4.1.12 Purpose of System.Exception
🔹 Standardizes error handling in C#.
🔹 Allows developers to catch and process exceptions easily.
🔹 Enables applications to continue running despite errors.
🔹 Supports exception chaining using InnerException.
4.1.13 Key Properties of System.Exception
Message → Stores the error description.
StackTrace → Shows where the error originated.
InnerException → Stores nested exceptions for debugging.
Source → Identifies the assembly or application causing the error.
Data → Stores additional information as key-value pairs.
Example:
try
throw new Exception("A basic exception occurred.");
catch (Exception ex)
Console.WriteLine($"Error: {ex.Message}");
Console.WriteLine($"Stack Trace: {ex.StackTrace}");
4.1.14 Constructors of System.Exception
Default Constructor → Creates an empty exception.
28
Parameterized Constructor → Accepts a custom error message.
Constructor with InnerException → Wraps another exception inside.
Example:
try
throw new Exception("Primary exception.");
catch (Exception ex)
throw new Exception("Secondary error occurred.", ex);
4.1.15 Common Methods in System.Exception
🔍 ToString() → Returns a string representation of the exception.
🔍 GetBaseException() → Retrieves the root cause of the error.
🔍 HelpLink → Stores a link to additional help resources.
Example:
try
{
throw new Exception("Main error.");
}
catch (Exception ex)
{
Console.WriteLine(ex.GetBaseException().Message);
}
4.2 Checked vs. Unchecked Exceptions
Checked vs. Unchecked Exceptions in C#
📌 C# does not enforce checked exceptions like Java.
29
📌 Exceptions in C# are runtime exceptions—handled dynamically.
📌 Checked vs. Unchecked exceptions help classify errors for better handling.
4.2.1 Checked Exceptions
🔹 Checked exceptions occur due to external issues (e.g., file or network failures).
🔹 They must be handled using try-catch to prevent program failure.
🔹 Examples include:
o FileNotFoundException → Occurs when a file is missing.
o IOException → Happens due to I/O operation failures.
o FormatException → Invalid input format errors.
Example:
try
{
int number = int.Parse("InvalidData");
}
catch (FormatException ex)
{
Console.WriteLine("Handled FormatException: " + ex.Message);
}
4.2.2 Unchecked Exceptions
🔹 Unchecked exceptions arise due to coding mistakes (e.g., null references).
🔹 They are not enforced by the compiler but occur at runtime.
🔹 Examples include:
o NullReferenceException → When accessing an object that is null.
o IndexOutOfRangeException → When exceeding array limits.
o DivideByZeroException → Division by zero errors.
Example:
try
{
int result = 5 / 0; // Causes DivideByZeroException
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Handled: " + ex.Message);
}
Key Differences
30
✅ Checked Exceptions → Must be caught or handled explicitly. ✅ Unchecked Exceptions →
Occur due to programming errors. ✅ Checked Exceptions → Typically result from external
sources. ✅ Unchecked Exceptions → Occur due to incorrect logic in code.
4.3 Exception Inheritance Structure
5. Handling Multiple Exceptions
Using Multiple catch Blocks
Filtering Exceptions Using catch when
Nested Try-Catch Blocks
5.1 Using Multiple catch Blocks in C#
📌 catch blocks allow handling multiple exception types separately.
📌 Each catch block targets a specific exception based on type.
📌 If an exception matches a catch block, that block executes.
📌 If no matching catch block exists, the exception remains unhandled.
31
Key Benefits of Using Multiple catch Blocks
🔹 Enables precise error handling for different exception types.
🔹 Reduces reliance on generic exception handling (Exception).
🔹 Prevents overgeneralization of exceptions, improving debugging
clarity.
🔹 Allows developers to respond differently to various failure
scenarios.
Basic Syntax of Multiple catch Blocks
csharp
try
{
int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[5]); // Causes IndexOutOfRangeException
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("Handled: Array index out of range.");
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Handled: Division by zero detected.");
}
catch (Exception ex)
{
Console.WriteLine("Handled: General exception occurred.");
}
Execution Flow of Multiple catch Blocks
✅ The first matching catch block executes, skipping others.
✅ Exception order matters—more specific exceptions should be
caught first.
✅ A general catch block (Exception) should come last to handle
unknown exceptions.
Example Flow:
try
{
int num = 5 / 0; // Causes DivideByZeroException
}
catch (DivideByZeroException ex) // ✅ First match (executes)
{
32
Console.WriteLine("Error: Cannot divide by zero.");
}
catch (Exception ex) // ❌ Skipped (already handled above)
{
Console.WriteLine("General Exception caught.");
}
Order of catch Blocks Matters
✅ Start with specific exceptions, moving to general ones.
✅ Avoid placing Exception first, as it catches all errors.
✅ Prioritize critical exceptions before broader ones.
Example of Incorrect Order:
csharp
try
{
int num = int.Parse("abc"); // Causes FormatException
}
catch (Exception ex) // ❌ First catch block (catches everything)
{
Console.WriteLine("General Exception caught.");
}
catch (FormatException ex) // ❌ Never executes (already caught above)
{
Console.WriteLine("FormatException handled.");
}
Example of Correct Order:
csharp
try
{
int num = int.Parse("abc"); // Causes FormatException
}
catch (FormatException ex) // ✅ Catches first (executes)
{
Console.WriteLine("Handled: Invalid number format.");
}
catch (Exception ex) // ✅ Executes only if previous blocks fail
{
Console.WriteLine("Handled: General Exception.");
}
33
5.2 Filtering Exceptions Using catch when in C#
📌 catch when allows filtering exceptions based on specific conditions.
📌 Unlike regular catch, it executes only when the condition is true.
📌 Helps in dynamic exception handling by applying logic to errors.
📌 Reduces unnecessary exception handling when certain criteria are
met.
Key Features of catch when
🔹 Conditional Handling → Executes only when the specified condition
matches.
🔹 Selective Exception Processing → Skips exceptions that don't
meet the criteria.
🔹 Refines Error Handling → Avoids catching errors that don't need
to be processed.
🔹 Improves Debugging → Provides better control over when
exceptions are handled.
Basic Syntax of catch when
try
{
int number = int.Parse("InvalidData"); // Causes FormatException
}
catch (FormatException ex) when (ex.Message.Contains("Input string"))
{
Console.WriteLine("Filtered Exception: Invalid format detected.");
}
catch (Exception ex)
{
Console.WriteLine("General Exception caught.");
}
How catch when Works
✅ The condition inside when evaluates the exception details.
✅ If the condition is true, the exception is handled.
✅ If the condition is false, the exception bypasses the block and
moves to the next catch.
Example Flow:
try
{
throw new ArgumentException("Invalid Argument");
}
catch (ArgumentException ex) when (ex.Message.Contains("Invalid"))
34
{
Console.WriteLine("Handled: Argument Exception.");
}
catch (Exception ex)
{
Console.WriteLine("Unhandled: General Exception.");
}
Real-World Use Cases of catch when
✅ Logging Specific Exceptions → Only log critical errors, skipping minor
ones. ✅ Retry Mechanisms → Handle exceptions based on failure count. ✅
Network Errors → Catch connection errors only when retry attempts exceed
a limit. ✅ Security Filtering → Handle access-related errors based on user
roles.
Example:
int retryCount = 3;
try
{
throw new Exception("Connection failed.");
}
catch (Exception ex) when (retryCount > 2)
{
Console.WriteLine("Handled: Network error after multiple retries.");
}
.5.3 Basic Nested Try-Catch Block
Outer try-catch handles overall exceptions.
Inner try-catch catches specific errors inside the block.
The inner exception can be re-thrown to be handled at a higher level.
csharp
try
{
Console.WriteLine("Starting outer try block.");
// Inner try-catch for specific errors
try
{
int result = 5 / 0; // Causes DivideByZeroException
}
35
catch (DivideByZeroException ex)
{
Console.WriteLine("Inner Catch: Division by zero error.");
throw; // Re-throws exception to outer catch block
}
Console.WriteLine("This line is skipped after an inner error.");
}
catch (Exception ex)
{
Console.WriteLine("Outer Catch: Handling re-thrown exception.");
}
Console.WriteLine("Program execution continues after exception handling.");
💡 Description:
throw; inside the inner catch passes the exception to the outer
block.
The outer catch handles any re-thrown exceptions and ensures
program continuation.
2. Nested Try-Catch Handling Different Exceptions
Different types of exceptions are caught at different levels.
IndexOutOfRangeException occurs in the inner block.
Outer block ensures final handling of uncaught exceptions.
csharp
try
{
Console.WriteLine("Outer try block begins.");
try
{
int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[5]); // Causes IndexOutOfRangeException
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("Inner Catch: Array index out of bounds.");
throw; // Exception passed to outer block
}
Console.WriteLine("Outer try block resumes.");
36
}
catch (Exception ex)
{
Console.WriteLine("Outer Catch: Handling thrown exception.");
}
💡 Description:
Inner try block throws an IndexOutOfRangeException.
Inner catch handles it but re-throws it using throw;.
Outer catch then catches the error and handles it properly.
3. Deeply Nested Try-Catch Blocks
Errors bubble up through multiple nested levels.
Each level of catch handles errors and can choose to re-throw or
suppress them.
csharp
try
{
Console.WriteLine("Executing outer try block.");
try
{
try
{
throw new NullReferenceException("Inner-most error");
}
catch (NullReferenceException ex)
{
Console.WriteLine("Innermost Catch: Handling
NullReferenceException.");
throw; // Passes the error up one level
}
}
catch (Exception ex)
{
Console.WriteLine("Middle Catch: Caught an exception and re-
throwing.");
throw; // Passes the error up to the outer block
}
}
catch (Exception ex)
{
37
Console.WriteLine("Outer Catch: Final handling of exception.");
}
Console.WriteLine("Program execution resumes after handling exceptions.");
💡 Description:
Innermost block throws a NullReferenceException.
The middle block catches it, logs an error message, and passes it
up.
The outer block catches the final exception, ensuring complete
error handling.
6. Creating and Using Custom Exceptions
Defining Custom Exception Classes
Implementing Custom Exception Handling
Adding Custom Properties and Methods
6.1 Definition of a Custom Exception Class in C#
📌 A custom exception is a user-defined error class that extends
the built-in Exception class.
📌 It is used to handle application-specific error conditions that
built-in exceptions do not cover.
📌 Custom exceptions improve code readability, debugging, and
error classification.
📌 They allow developers to define specialized exception types with
custom properties and behaviors.
📌 A custom exception must inherit from Exception or a derived class
like System.ApplicationException (deprecated).
6.2 Implementation of Custom Exception Handling in C#
using System;
class Program
{
static void Main()
{
try
{
ValidateAge(15); // Call method that throws a custom exception
38
}
catch (AgeValidationException ex)
{
Console.WriteLine($"Custom Exception Caught: {ex.Message}");
}
}
static void ValidateAge(int age)
{
if (age < 18)
{
throw new AgeValidationException("Age must be 18 or older.");
}
Console.WriteLine("Age is valid.");
}
}
// Custom exception class
public class AgeValidationException : Exception
{
public AgeValidationException(string message) : base(message) { }
}
💡 Explanation:
🔹 Custom Exception Class (AgeValidationException) is defined by
inheriting from Exception.
🔹 ValidateAge Method throws the custom exception if age is below 18.
🔹 try-catch Block in Main handles the custom exception when thrown.
6.3 Adding Custom Properties and Methods to a Custom Exception
in C#
using System;
// Custom exception class with additional properties and methods
public class AgeValidationException : Exception
{
// Custom property
public int InvalidAge { get; }
// Constructor with custom property initialization
39
public AgeValidationException(string message, int age) : base(message)
{
InvalidAge = age;
}
// Custom method to get formatted error details
public string GetExceptionDetails()
{
return $"Error: {Message}, Invalid Age: {InvalidAge}";
}
}
// Main program demonstrating exception handling with custom properties
and methods
class Program
{
static void Main()
{
try
{
ValidateAge(15); // Passing an invalid age
}
catch (AgeValidationException ex)
{
Console.WriteLine($"Custom Exception Caught:
{ex.GetExceptionDetails()}");
}
}
// Method that throws the custom exception
static void ValidateAge(int age)
{
if (age < 18)
{
throw new AgeValidationException("Age must be 18 or older.", age);
}
Console.WriteLine("Age is valid.");
}
}
Explanation of Key Enhancements in the Custom Exception Class
40
📌 Custom Property (InvalidAge) → Stores the invalid age that caused
the exception.
📌 Parameterized Constructor → Initializes both exception
message and custom property.
📌 Custom Method (GetExceptionDetails) → Returns detailed error
information.
📌 Exception Handling in try-catch Block → Retrieves custom error
details using a method.
Benefits of Adding Custom Properties and Methods
✅ Enhances debugging → Developers can track the exact value causing an
error. ✅ Provides structured error information → Messages are more
informative than generic ones. ✅ Encapsulates custom logic → Methods
define reusable exception-handling behaviors. ✅ Supports advanced error
logging → Easy to log detailed exception data in real-world applications.
7. Exception Propagation and Best Practices
Exception Propagation Across Methods
Best Practices in Exception Handling
Writing Meaningful Error Messages
Avoiding Overuse of Try-Catch Blocks
7.1 Exception Propagation Across Methods
📌 Exception propagation refers to how exceptions move from one
method to another.
📌 When a method encounters an exception, it can either handle it or
propagate it to the caller.
📌 If an exception is not caught in the current method, it moves up the
call stack.
📌 This allows higher-level methods to handle exceptions more
effectively.
Example:
void MethodA()
{
MethodB(); // Calls MethodB, which throws an exception
}
41
void MethodB()
{
throw new Exception("Error occurred in MethodB."); // Unhandled
exception propagates
}
MethodA(); // Exception moves up to the main caller
💡 Key takeaway: If MethodB does not catch the exception, it propagates
to MethodA, which must handle it.
7.2 Best Practices in Exception Handling
✅ Use specific exception types instead of catching Exception. ✅
Implement meaningful error messages to improve debugging. ✅ Log
exceptions properly using logging frameworks like Serilog or NLog. ✅ Use
finally blocks for cleanup operations like closing database connections. ✅
Avoid swallowing exceptions by keeping empty catch blocks. ✅ Ensure
exception propagation when a lower-level method cannot handle the error
properly.
Example of logging exceptions:
try
{
throw new InvalidOperationException("Invalid operation performed.");
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"Error: {ex.Message}"); // Log exception details
}
7.3 Writing Meaningful Error Messages
Error messages should clearly describe the problem.
Avoid generic messages like "Something went wrong"—make it
specific.
Provide context in messages, such as method names or failed
conditions.
Error messages should help debugging, not confuse developers.
Example:
csharp
throw new ArgumentException("User age cannot be negative. Please provide
a valid age.");
42
💡 Key takeaway: A well-written message immediately tells what the issue
is and how to fix it.
7.4 Avoiding Overuse of Try-Catch Blocks
❌ Do not wrap every block of code in try-catch unnecessarily. ❌ Use
exceptions for unexpected errors, not for control flow. ❌ Rely on
validations instead of throwing exceptions for simple checks. ❌ Only catch
exceptions where handling is required—let unhandled exceptions
propagate if needed.
Example of overuse (bad practice):
try
{
if (age < 0)
throw new ArgumentException("Invalid age."); // Unnecessary exception
}
catch (ArgumentException ex)
{
Console.WriteLine("Handled error: " + ex.Message); // Could be avoided
with validation
}
Example of good practice:
csharp
if (age < 0)
{
Console.WriteLine("Age cannot be negative."); // Prevent exception using
validation
8. Exception Handling in Delegates and Events
Managing Exceptions in Event-Driven Programming
Exception Handling in Callback Functions
8.1 Managing Exceptions in Event-Driven Programming
📌 In event-driven programming, exceptions can occur inside event
handlers.
📌 If an event handler throws an exception, it may disrupt the event
execution flow.
📌 Unhandled exceptions in event-driven systems may cause the
program to crash unexpectedly.
43
📌 Exception handling ensures that errors in event handlers do not
affect other subscribers.
📌 Best practices include catching exceptions within event handlers
and using logging mechanisms.
Example:
csharp
public class EventPublisher
{
public event EventHandler MyEvent;
public void RaiseEvent()
{
try
{
MyEvent?.Invoke(this, EventArgs.Empty);
}
catch (Exception ex)
{
Console.WriteLine($"Exception in event handler: {ex.Message}");
}
}
}
💡 Key takeaway: Exceptions inside event handlers should be caught to
prevent breaking the event invocation process.
8.2 Exception Handling in Callback Functions
📌 Callback functions are methods passed as arguments to other
functions for execution.
📌 If a callback function throws an exception, it may disrupt the
execution of the calling function.
📌 Exception handling ensures that errors do not affect subsequent
callbacks.
📌 Wrapping callback execution in a try-catch block prevents program
crashes.
📌 Logging errors inside the callback helps debugging without
stopping execution.
Example:
public delegate void CallbackDelegate(string message);
public class CallbackHandler
44
{
public void ExecuteCallback(CallbackDelegate callback)
{
try
{
callback("Executing callback function...");
}
catch (Exception ex)
{
Console.WriteLine($"Exception in callback: {ex.Message}");
}
}
}
9. Global Exception Handling in C#
Using AppDomain.UnhandledException
Implementing Global Exception Handling in Web Applications (ASP.NET)
9.1 Using AppDomain.UnhandledException in C#
• Using AppDomain.UnhandledException • Implementing Global Exception
Handling in Web Applications (ASP.NET)
Using AppDomain.UnhandledException in C#
📌 AppDomain.UnhandledException provides a global error-handling
mechanism for unhandled exceptions.
📌 It catches exceptions that are not handled in any try-catch block,
preventing program crashes.
📌 This handler is useful in console applications and desktop
applications for centralized exception management.
📌 Setting up an UnhandledException event helps log errors before the
application exits.
Example:
class Program
{
static void Main()
{
AppDomain.CurrentDomain.UnhandledException +=
GlobalExceptionHandler;
throw new Exception("Unhandled error!");
45
}
static void GlobalExceptionHandler(object sender,
UnhandledExceptionEventArgs e)
{
Console.WriteLine($"Global Exception Caught:
{((Exception)e.ExceptionObject).Message}");
}
}
💡 Key takeaway: Exceptions not handled elsewhere will trigger this global
handler, preventing unexpected application termination.
9.2 Implementing Global Exception Handling in Web Applications
(ASP.NET)
📌 In ASP.NET applications, global exception handling ensures all
unhandled errors are logged and managed centrally.
📌 It prevents users from seeing raw system errors and redirects them
to an error page.
📌 Common methods for handling exceptions globally in ASP.NET:
o Middleware Exception Handling (UseExceptionHandler)
o Custom Error Handling in Global.asax
o Exception Filters (IExceptionFilter)
Example using middleware (UseExceptionHandler):
public void Configure(IApplicationBuilder app)
{
app.UseExceptionHandler("/ErrorPage"); // Redirects to error page on
exception
}
Example using global filters in ASP.NET Core:
csharp
public class GlobalExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
context.Result = new ObjectResult("Global error handled.")
{ StatusCode = 500 };
}
}
46
10. Debugging and Error Recovery Strategies
Using Debuggers (Visual Studio Exception Settings)
Graceful Degradation and Recovery
10.1 Using Debuggers (Visual Studio Exception Settings)
📌 Debuggers help developers track and fix errors efficiently in C#.
📌 Visual Studio Exception Settings allow customizing exception
handling.
📌 Developers can enable breaking on specific exceptions, skipping
handled exceptions, and setting custom debugger rules.
📌 Breakpoints & Watch Windows help monitor variable values at
runtime for deeper analysis.
📌 Exception Settings Window in Visual Studio allows choosing when
exceptions should break execution.
Example debugging steps in Visual Studio: 1️⃣ Open Exception
Settings (Debug → Windows → Exception Settings). 2️⃣ Enable specific
exceptions to break execution when thrown. 3️⃣ Use the Immediate
Window (Ctrl+D, I) to test and evaluate expressions. 4️⃣ Set conditional
breakpoints for debugging particular error scenarios.
💡 Key takeaway: Debuggers provide better visibility into where and why
exceptions occur, helping improve application reliability.
10.2 Graceful Degradation and Recovery
📌 Graceful degradation ensures a system continues functioning
despite errors.
📌 Applications should prevent crashes by intelligently handling
unexpected errors.
📌 Recovery strategies include fallback mechanisms, default values,
and retry attempts.
📌 A common practice is logging exceptions while keeping essential
functionality accessible.
📌 Recovery can involve retrying failed operations or switching to a
safer execution mode.
Example of graceful error recovery:
csharp
47
try
{
int result = 10 / 0; // Causes DivideByZeroException
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Error detected, switching to safe mode.");
int result = 1; // Fallback value to maintain program stability
}
11 Summery
Exception handling in C# is a vital feature that ensures applications remain
stable and responsive even when unexpected errors occur. By using try,
catch, finally, and throw blocks, developers can structure their code to
detect, manage, and recover from runtime issues effectively.
Through proper implementation of exception handling, software becomes
more reliable and maintainable, reducing the risk of abrupt failures.
Understanding the hierarchy of exceptions and leveraging built-in exception
classes allows developers to address errors efficiently while providing
meaningful feedback to users.
Effective exception handling practices, such as using specific exception
types, logging errors, and avoiding excessive catch blocks, contribute to a
well-structured and resilient application. By mastering these techniques,
developers can build software that adapts to various runtime challenges
while ensuring a smooth user experience.
48
12 References
1. Microsoft Docs. (2023). Exception Handling (C# Programming Guide).
[https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/
exceptions/](https://learn.microsoft.com/en-us/dotnet/csharp/programming-
guide/exceptions/)
2. Microsoft Docs. (2023). Debugging in Visual Studio.
[https://learn.microsoft.com/en-us/visualstudio/debugger/](https://
learn.microsoft.com/en-us/visualstudio/debugger/)
3. Troelsen, A., & Japikse, P. (2022). Pro C# 10 with .NET 6. Apress.
- Chapter 7: "Exception Handling and Debugging."
4. Richter, J. (2012). CLR via C# (4th ed.). Microsoft Press.
- Chapter 20: "Exceptions and State Management."
5. McConnell, S. (2004). Code Complete: A Practical Handbook of Software
Construction(2nd ed.). Microsoft Press.
- Section on Defensive Programming and Error Handling.
6. Freeman, A., & Jones, A. (2021). *C# 10 and .NET 6 – Modern Cross-
Platform Development. Packt.
- Chapter 4: "Debugging and Exception Handling."
49