C# & .NET Framework The .NET Grand Finale
💡
Exercise 47

Exception Handling — The City's Disaster Recovery 20 XP Medium

Ctrl+Enter Run Ctrl+S Save

🛡️ Exception Handling in C#

Even the best-planned cities face disasters — earthquakes, fires, budget overruns. A well-governed city doesn't pretend bad things won't happen; it builds disaster recovery systems.

In C#, exceptions are the runtime's way of saying "something went wrong." Exception handling gives you structured tools to catch errors, recover gracefully, and clean up resources.

🧱 try / catch / finally

try — wraps code that might throw an exception.

catch — handles the exception if one occurs.

finally — runs always, whether or not an exception was thrown. Perfect for cleanup.

try { int result = 10 / 0; // Throws DivideByZeroException } catch (DivideByZeroException ex) { Console.WriteLine($"Caught: {ex.Message}"); } finally { Console.WriteLine("Cleanup complete"); }

📋 Common Exception Types

  • NullReferenceException — accessing a member on a null object.
  • ArgumentException / ArgumentNullException — invalid method arguments.
  • InvalidOperationException — operation not valid for current state.
  • IndexOutOfRangeException — array index out of bounds.
  • FormatException — string isn't in the correct format for parsing.
  • IOException — I/O errors (file not found, disk full, etc.).

🎯 Exception Filters (when clause)

C# 6+ lets you add a when condition to a catch block, so it only catches exceptions matching a specific criterion:

try { ProcessOrder(order); } catch (InvalidOperationException ex) when (ex.Message.Contains("budget")) { Console.WriteLine("Budget issue — adjusting..."); } catch (InvalidOperationException ex) { Console.WriteLine($"Other issue: {ex.Message}"); }

🔨 Custom Exceptions

For domain-specific errors, create your own exception class by inheriting from Exception:

class PermitExpiredException : Exception { public PermitExpiredException(string message) : base(message) { } public PermitExpiredException(string message, Exception inner) : base(message, inner) { } }

🚨 throw vs throw ex

  • throw; — rethrows the current exception, preserving the original stack trace.
  • throw ex; — resets the stack trace to the current point. Avoid this — it destroys debugging information.
  • throw new CustomException("msg", ex); — wraps the original exception as an inner exception. Best practice for custom exceptions.

📐 The Exception Hierarchy

All exceptions inherit from System.Exception. Catch more specific exceptions first, then more general ones. Catching bare Exception is a last resort — it catches everything, including critical system errors you shouldn't swallow.

try { /* ... */ } catch (FileNotFoundException ex) { /* specific */ } catch (IOException ex) { /* less specific */ } catch (Exception ex) { /* last resort */ } finally { /* always runs */ }
📋 Instructions
**The City's Disaster Recovery System** Build a program that demonstrates exception handling: 1. Print `"Attempting construction..."` 2. In a `try` block, throw a new `InvalidOperationException("Budget exceeded!")` 3. `catch` it and print `"Error caught: Budget exceeded!"` 4. Print `"Recovery: Adjusting budget..."` 5. In the `finally` block, print `"Finally: Cleanup complete"` 6. After the try/catch/finally, create a second try/catch: - Throw a new `PermitExpiredException("Permit has expired")` - Catch `PermitExpiredException` and print `"Custom error: PermitExpiredException handled"` Define `PermitExpiredException` as a class inheriting from `Exception` with a constructor that takes a `string message`.
Define `class PermitExpiredException : Exception` with a constructor passing `message` to `base(message)`. In Main, use try { throw new InvalidOperationException(...); } catch (InvalidOperationException ex) { print message + recovery } finally { print cleanup }. Then a second try/catch for the custom exception.
main.py
Hi! I'm Rex 👋
Output
Ready. Press ▶ Run or Ctrl+Enter.