Exercises: Exception Handling
Part A: Conceptual Questions
A1. Explain the difference between try..except and try..finally. When would you use each? When would you use both together?
A2. Why is "swallowing exceptions" (catching and ignoring them) considered a dangerous antipattern? Give a concrete scenario where this could cause real-world harm.
A3. Explain why catching Exception (the base class) is usually too broad. In what situation might catching the base Exception class be appropriate?
A4. Why should exceptions not be used for normal flow control? What is the performance consideration, and what is the readability consideration?
A5. Compare the IOResult approach to error handling with the exception-based approach. List three advantages of exceptions over IOResult.
A6. Explain the "write-to-temp-then-rename" pattern for safe file saving. Why is it safer than writing directly to the original file?
A7. Why is it important to order on clauses from most specific to most general? What happens if you put a generic Exception handler before a specific EConvertError handler?
Part B: Tracing and Prediction
B1. What is the output of the following code?
procedure Inner;
begin
WriteLn('Inner: before raise');
raise Exception.Create('Boom!');
WriteLn('Inner: after raise'); { Does this execute? }
end;
procedure Middle;
begin
WriteLn('Middle: before Inner');
Inner;
WriteLn('Middle: after Inner'); { Does this execute? }
end;
begin
WriteLn('Main: before try');
try
Middle;
except
on E: Exception do
WriteLn('Caught: ', E.Message);
end;
WriteLn('Main: after try');
end.
B2. What is the output of this code?
begin
try
WriteLn('A');
try
WriteLn('B');
raise Exception.Create('Error');
WriteLn('C');
finally
WriteLn('D');
end;
WriteLn('E');
except
on E: Exception do
WriteLn('F: ', E.Message);
end;
WriteLn('G');
end.
B3. What is the output of this code? (Trace carefully — there are two nested try blocks.)
procedure ProcessItem(N: Integer);
begin
if N = 0 then
raise EDivByZero.Create('Division by zero');
WriteLn(100 div N);
end;
var
i: Integer;
begin
for i := 3 downto -1 do
begin
try
Write('Processing ', i, ': ');
ProcessItem(i);
except
on E: EDivByZero do
WriteLn('Skipped — ', E.Message);
on E: Exception do
WriteLn('Unknown error: ', E.Message);
end;
end;
WriteLn('Done.');
end.
Part C: Short Programming Exercises
C1. Safe Division Function
Write a function SafeDiv(A, B: Integer): Integer that returns A div B, or returns 0 and prints a warning if B is zero. Use exception handling, not an if check.
C2. Robust String-to-Integer Array
Write a function ParseIntArray(const Input: String): TIntArray (where TIntArray = array of Integer) that takes a comma-separated string of numbers like '10,20,abc,30,xyz,40' and returns an array of successfully parsed integers, printing a warning for each invalid value. Use exceptions to detect invalid values.
C3. File Copy with Error Handling
Write a procedure SafeFileCopy(const Source, Dest: String) that copies a text file line by line. Handle the following cases with appropriate exceptions:
- Source file does not exist
- Destination directory does not exist
- Read error during copy
Ensure all files are properly closed using try..finally.
C4. Input Validation Loop
Write a procedure ReadPositiveFloat(const Prompt: String; out Value: Double) that repeatedly prompts the user until they enter a valid positive floating-point number. Handle EConvertError for non-numeric input and raise a custom ENegativeValueError for negative numbers.
C5. Custom Exception Hierarchy
Design and implement a custom exception hierarchy for a library system:
- ELibraryError (base)
- EBookNotFoundError (includes book title)
- EBookCheckedOutError (includes book title and borrower name)
- EMaxBooksExceededError (includes member name and max count)
Write a TLibrary class with methods CheckOut and Return that raise these exceptions appropriately. Write a main program that demonstrates catching each exception type.
Part D: Design Challenges
D1. Transaction-Safe Operations
Design a TTransactionLog class that records operations. If an exception occurs during a series of operations, the log should "roll back" by undoing completed operations. Implement BeginTransaction, CommitTransaction, and RollbackTransaction methods. Write a demonstration showing a transaction that succeeds and one that fails partway through.
D2. Robust CSV Parser
Write a procedure ParseCSVFile(const FileName: String) that reads a CSV file where each row should have exactly 4 columns: Name (string), Age (integer), Grade (float), Passed (boolean: 'true'/'false'). Handle all possible errors:
- File not found
- Wrong number of columns in a row
- Invalid integer/float/boolean values
- Empty rows
For each row, either process it successfully or print a detailed error message and skip to the next row. At the end, print a summary: "Processed X of Y rows successfully."
D3. Retry Pattern
Write a generic RetryOperation procedure that takes a procedure variable and retries it up to N times if it raises an exception, waiting progressively longer between retries. After all retries are exhausted, re-raise the last exception. Demonstrate with a simulated network operation that fails randomly.