Types of Errors – Review from Debugging
- Syntax errors: compile time issues, indicated by red squiggles, prevent program execution.
- Example: Typographical errors like forgetting to capitalize “L” in
WriteLine.
- Execution time errors: cause the program to crash during runtime.
- Example: Attempting to convert a non-integer string input to an integer.
- Program logic errors: the program runs without crashing but produces incorrect results.
- Example: Calculating an average by dividing the sum by an incorrect number (e.g., dividing by 5 instead of 3).
- Expecting an output of 4.0 ( (3+4+5=12/3=4)) but getting 2.4.
Exceptions
- Exception: An object of a class generated due to an execution-time error or unexpected event.
- Exceptions are "thrown" during such events.
- Unhandled exceptions typically crash the program.
Handling Exceptions
- Exception handler: C# code that responds to exceptions gracefully (i.e., without crashing).
- Graceful handling includes:
- Correcting the problem and continuing execution.
- Displaying a message to alert the user for corrective action.
- Saving information, displaying a clear error message, and terminating the program.
- Exception handling: The process of intercepting and responding to exceptions.
- C# provides a default exception handler that displays an error message and crashes the program when an exception is not handled.
- It is recommended to handle any exceptions that may occur.
Types of Exceptions
- C# defines numerous exception types, forming a hierarchy of Exception classes.
- Exception: The most general type, encompassing all other exception types.
- DivideByZeroException: Occurs when dividing by zero.
- FormatException: Occurs when something is poorly formatted.
- Example: Passing the string “abc” to
int.Parse.
- IndexOutOfRangeException: Occurs when trying to access an array element outside its bounds.
- NullReferenceException: Occurs when attempting to call a method on a null object.
- Custom exception types can also be defined.
Handling Exceptions with try-catch Block
- The
try-catch block is used to handle exceptions in C#.
try {
// Code that may crash goes here…
} catch(<Exception type> <parameter name>) {
// Code for what to do instead of crashing goes here…
}
try Block
- The
try block contains code that will be attempted and monitored for exceptions. - Correct code can encounter exceptional conditions due to:
- Bad user input.
- Out of memory errors.
- Disk issues (full, read-only, offline).
- Hardware failures.
- Network problems.
- Security issues blocking access.
- The
try block contains one or more code statements that may throw an exception. - If an exception is thrown within the
try block, the program will intercept and handle it in the catch block. - If code outside of a
try block throws an exception, it cannot be handled by a catch block, leading to program termination.
catch Block
- Each
try block is followed by one or more catch blocks. - A
catch block starts with:catch (<Exception type> <parameter name>)Exception type: The name of the exception class (e.g., Exception, FormatException).Parameter name: A name to refer to the exception object within the catch block (commonly "e" or "ex").- The exception type and parameter name are optional; you can use just the
catch keyword.
- The code within a
catch block is executed only if the try block throws an exception.
Exception Handling Flow
- If an exception is thrown within a
try block:- The corresponding
catch block is executed. - After the
catch block, the program continues executing the next line following the catch block.
- If no exception is thrown within a
try block:- The
catch block is skipped. - The program continues executing the next line following the
catch block.
Example: Without Exception Handling
- Without exception handling, the default exception handler is called, which crashes the program.
- An
IndexOutOfRangeException occurs when trying to access an array element with an invalid index.
Example: With Exception Handling
- An array
numbers is created with three elements: 3,9,2. - A
try block attempts to iterate through the array and print each element. - A
catch block handles the IndexOutOfRangeException that occurs when the loop tries to access an invalid index. - The program prints a message indicating the subscript is beyond the array's size and continues execution.
Retrying When Exception is Thrown
- A
while loop is used to continuously prompt the user for an integer input until a valid integer is entered. - The
try block attempts to parse the user's input using int.Parse(). - If the parsing fails (i.e., a
FormatException is thrown), the catch block is executed, informing the user to try again. - If the parsing is successful, the
ok flag is set to true, and the loop exits.
Exceptions – Message Field
- Exceptions are objects with fields and methods.
- Each exception object has a
Message field.Message: Retrieves a message indicating what error occurred.
Polymorphic References to Exceptions
- Exception classes form a hierarchy (inheritance or "is-a" relationship).
- A polymorphic reference can be used as a parameter in the
catch block. - Catching a type of exception also catches any exception type below it in the hierarchy.
- Example:
ArgumentNullException and ArgumentOutOfRangeException are derived from ArgumentException, which is derived from SystemException, which is derived from Exception. - Catching
ArgumentException will catch ArgumentException, ArgumentNullException, and ArgumentOutOfRangeException. - Catching
Exception will catch any exception type.
Polymorphic References to Exceptions - Examples
- Example 1: Catching Exception
int number;
try
{
number = int.Parse(Console.ReadLine()); // throws a FormatException, if non-integer input given
}
catch (Exception ex) // FormatException is derived from Exception, this catch block will catch it
{
Console.WriteLine($"The following error occurred: {ex.Message}");
}
- Example 2: Catching FormatException
int number;
try
{
number = int.Parse(Console.ReadLine()); // throws a FormatException, if non-integer input given
}
catch (FormatException ex) // FormatException is the exception that might be thrown, it will be caught
{
Console.WriteLine($"The following error occurred: {ex.Message}");
}
- Example 3: Catching IndexOutOfRangeException (Will not be caught)
int number;
try
{
number = int.Parse(Console.ReadLine()); // throws a FormatException, if non-integer input given
}
catch (IndexOutOfRangeException ex) // IndexOutOfRange Exception is unrelated to FormatException, it will not be caught
{
Console.WriteLine($"The following error occurred: {ex.Message}");
}
Handling Multiple Exceptions
- The code within the
try block may throw more than one type of exception. - Different exceptions may need to be handled differently using multiple
catch blocks. catch blocks must be listed from most specific (lower on the hierarchy) to most general (higher on the hierarchy) exception type.- There cannot be multiple
catch blocks for the same exception type. - Only one
catch block is executed.
Handling Multiple Exceptions - Invalid Examples
- You cannot have two
catch blocks for the same exception type because it is ambiguous which one should be executed. - You cannot list a more general exception type before a more specific one because the more general handler would catch everything that the second handler could catch.
Handling Multiple Exceptions - Valid Example
int number;
string str = "";
try
{
str = Console.ReadLine();
number = int.Parse(str); // throws a FormatException, if non-integer input given
}
catch (FormatException ex) // more specific
{
Console.WriteLine($"{str} is not a number.");
}
catch (SystemException ex) // more general
{
Console.WriteLine("Bad number format.");
}
- If the exception types are at the same level in the hierarchy, their order does not matter.
Handling Exceptions – finally block
- The
finally block is optional and can be included after the last catch block in a try-catch structure. - The code within the
finally block is always executed, regardless of whether an exception was thrown or not.
try {
// Code that may crash goes here…
} catch(<Exception type> <parameter name>) {
// Code for what to do instead of crashing goes here…
} finally {
// Code to execute, regardless of whether an exception occurs
}
- A common use case is ensuring a file gets closed.
- Even when an unhandled exception occurs, causing the program to crash, the
finally block will still execute before the program terminates.
The Call Stack and Stack Trace
- When a program executes, the system tracks the execution history.
- Example: If method
m1 invokes method m2, the system remembers where it was in m1 to return when m2 finishes.
- If a program crashes, the execution history tells you where the crash occurred and how the program got there.
- Call stack: An internal list of all methods currently executing.
- Stack trace: A list of all methods in the call stack (the execution history).
- Reviewing the stack trace allows you to determine:
- The method executing when the exception occurred.
- The methods called to reach that point.
The Call Stack and Stack Trace - Details
- The stack trace is displayed when the default exception handler is called.
- The stack trace is also accessible through the exception object’s
StackTrace field.
Uncaught Exceptions
- When an exception is thrown, it cannot be ignored.
- It must be handled by the program or the default exception handler.
- When code in a method throws an exception:
- Normal execution of that method stops.
- C# searches for a compatible exception handler inside the method.
- If no matching handler exists in the method:
- Control is passed back to the previous method in the call stack (the invoking method).
- If that method has no handler, control is passed up the call stack again.
- If control reaches the
Main method:- The
Main method must handle the exception, or - The program halts, and the default exception handler takes over.
Throwing Exceptions
- You can throw your own exceptions when an exceptional condition is detected using the
throw keyword. - The exception can be one of the built-in C# exception types (e.g.,
Exception, FormatException) or a custom type. - The parameter passed to
throw is the value for the Message field of the exception object.
Defining Your Own Exception Types
- Defining custom exception types requires inheritance from the
Exception class. - You need to define a new class that inherits from
Exception and provide definitions for the constructors. - You can then throw exceptions of this type when needed.
- Example: Throwing an
AgeException in a Person's age setter if the user enters an invalid age. - Exceptions provide a better way of handling invalid input in classes' setters instead of defaulting to some arbitrary value.