.NET ProgrammingUnit 414 min read

Exception Handling & Collections in C: Errors, Debugging & Data Structures

Unit 4 of .NET Programming covers how to handle runtime errors gracefully using try-catch-finally blocks, debug applications with stack traces, and work with built-in collections (Arrays, Lists, Dictionaries, Queues, Stacks) in C. Learn when to use each collection type, their time complexities, and real-world applicati

TAKEAWAYS:

  • Exception handling prevents crashes by catching errors (e.g., DivideByZeroException, FileNotFoundException) using try-catch-finally blocks.
  • Collections store groups of data efficiently: List<T> for dynamic arrays, Dictionary<TKey,TValue> for key-value pairs, Queue<T> for FIFO operations.
  • Debugging uses Debug.WriteLine() and stack traces to trace execution paths and identify errors in complex applications.
  • LINQ (covered in Unit 7) can filter/sort collections, but collections themselves are the foundation for storing and manipulating data.
  • Performance matters: List<T> has O(1) access but O(n) insertion/deletion in the middle, while Dictionary<TKey,TValue> offers O(1) lookups.
  • Real-world tie: eSewa uses Dictionary<string, User> to map user IDs to account details for fast authentication.

1. Exception Handling: Preventing Crashes

Errors in programs are inevitable. Exception handling ensures your application doesn’t crash unexpectedly by gracefully managing runtime errors.

Key Exceptions in C#

Exception Type Cause Example Scenario
NullReferenceException Accessing a null object string name = null; Console.WriteLine(name.Length);
DivideByZeroException Division by zero int result = 10 / 0;
FileNotFoundException File does not exist File.ReadAllText("nonexistent.txt");
IndexOutOfRangeException Array/list index out of bounds int[] arr = {1, 2}; Console.WriteLine(arr[2]);
InvalidOperationException Invalid method call (e.g., on closed stream) stream.Close(); stream.Read();

How try-catch-finally Works

try {
    // Risky code (may throw an exception)
    int result = 10 / int.Parse("0"); // Throws DivideByZeroException
}
catch (DivideByZeroException ex) {
    // Handle the specific exception
    Console.WriteLine($"Error: {ex.Message}");
}
catch (FormatException ex) {
    // Handle another possible exception
    Console.WriteLine($"Invalid input: {ex.Message}");
}
finally {
    // Always executes (cleanup code)
    Console.WriteLine("Operation attempted.");
}

Trace of Execution:

Step Code Executed Exception Thrown Output
1 int.Parse("0") None (Parses "0" successfully)
2 10 / 0 DivideByZeroException Catches and prints "Error: ..."
3 finally block None Prints "Operation attempted."

Real-World Example: eSewa Payment Validation

eSewa checks for invalid transactions before processing:

try {
    double amount = double.Parse(userInput);
    if (amount <= 0) throw new ArgumentException("Amount must be positive.");
    ProcessPayment(amount);
}
catch (FormatException) {
    Console.WriteLine("Invalid amount format. Use numbers only.");
}
catch (ArgumentException ex) {
    Console.WriteLine(ex.Message);
}

Why this matters:

  • Prevents crashes during high-volume transactions.
  • Provides clear error messages to users (e.g., "Invalid amount").

2. Collections: Storing and Managing Data

Collections are data structures that store groups of objects. C# provides built-in collections in the System.Collections.Generic namespace.

Common Collections and Their Use Cases

Collection Type Key Features Time Complexity (Average) Real-World Example (Nepal)
List<T> Dynamic Array Resizable, indexed access O(1) access, O(n) insert/delete Daraz order history (stores user orders)
Dictionary<TKey,TValue> Hash Table Key-value pairs, fast lookups O(1) lookup/insert eSewa user database (ID → Account)
Queue<T> FIFO Queue First-In-First-Out (e.g., printing) O(1) enqueue/dequeue NTC call center (next customer)
Stack<T> LIFO Stack Last-In-First-Out (e.g., undo) O(1) push/pop Pathao driver navigation (undo route)
HashSet<T> Set Unique elements, no duplicates O(1) add/contains NEPSE stock symbols (unique ticker IDs)

2.1 List<T>: Resizable Arrays

How it works:

  • Underlying array doubles in size when full (amortized O(1) insertion).
  • Insertion/deletion in the middle is O(n) (shifts elements).
100151202303
List<int> after Insert(1, 15) (amortized O(1) insertion, O(n) middle shift)

Trace of Insert(1, 15):

Step Operation State After Step Time Complexity
1 Shift elements 1→3 right [10, _, 20, 30] O(n)
2 Insert 15 at index 1 [10, 15, 20, 30] O(1)

Real-World Example: Daraz Order Queue Daraz stores orders in a List<Order> to track pending deliveries:

List<Order> pendingOrders = new List<Order>();
pendingOrders.Add(new Order("DAR-123", "Laptop", 50000));
pendingOrders.Insert(0, new Order("DAR-124", "Phone", 30000)); // Urgent order

Why List<T>?

  • Fast access by index (e.g., pendingOrders[0] for the first order).
  • Dynamic resizing avoids manual array management.

2.2 Dictionary<TKey,TValue>: Key-Value Pairs

How it works:

  • Uses a hash table for O(1) lookups.
  • Collisions resolved via chaining (linked lists).
0Alice1Bob2—3—
Dictionary<string,int> with chaining (hash(k) = k[0] mod 4)

Trace of scores["Alice"]:

Step Operation Hash Calculation State After Step
1 Compute hash("Alice") 123456789 Bucket 123456789 → ["Alice":95]
2 Retrieve value Key match Returns 95

Real-World Example: eSewa User Authentication eSewa maps user IDs to account details for fast login:

Dictionary<string, User> users = new Dictionary<string, User>();
users.Add("USER123", new User("Alice", "alice@example.com"));
bool isValid = users.TryGetValue("USER123", out User user);

Why Dictionary<TKey,TValue>?

  • O(1) lookup for user validation (critical for performance).
  • Avoids linear searches through a List<User>.

2.3 Queue<T>: First-In-First-Out (FIFO)

How it works:

  • Enqueue: Add to the back (O(1)).
  • Dequeue: Remove from the front (O(1)).
ABFRONTREARoutin
Queue<string> after Enqueue("A"), Enqueue("B")

Trace of Enqueue/Dequeue:

Step Operation State After Step
1 Enqueue("A") ["A"]
2 Enqueue("B") ["A", "B"]
3 Dequeue() ["B"] (returns "A")

Real-World Example: NTC Call Center NTC uses a Queue<Customer> to manage customer calls:

Queue<Customer> callQueue = new Queue<Customer>();
callQueue.Enqueue(new Customer("John", "Complaint"));
Customer nextCustomer = callQueue.Dequeue(); // Serves John first

Why Queue<T>?

  • Ensures fairness (FIFO) in customer service.
  • Efficient for task scheduling (e.g., printing jobs).

2.4 Stack<T>: Last-In-First-Out (LIFO)

How it works:

  • Push: Add to the top (O(1)).
  • Pop: Remove from the top (O(1)).
12TOP
Stack<int> after Push(1), Push(2)

Trace of Push/Pop:

Step Operation State After Step
1 Push(1) [1]
2 Push(2) [1, 2]
3 Pop() [1] (returns 2)

Real-World Example: Pathao Driver Navigation Pathao uses a Stack<Route> to implement "undo" for navigation:

Stack<Route> navigationHistory = new Stack<Route>();
navigationHistory.Push(new Route("Kathmandu to Bhaktapur"));
navigationHistory.Push(new Route("Bhaktapur to Nagarkot"));
Route lastRoute = navigationHistory.Pop(); // Undo to Bhaktapur

Why Stack<T>?

  • Simple reversal of operations (e.g., undo/redo).
  • Used in expression evaluation (e.g., postfix notation).

3. Debugging with Debug.WriteLine and Stack Traces

Debugging helps identify where and why errors occur.

Using Debug.WriteLine

using System.Diagnostics;

Debug.WriteLine("Starting calculation...");
int result = 10 / int.Parse("0"); // Throws DivideByZeroException
Debug.WriteLine("Result: " + result); // Never reaches here

Output in Debug Window:

Starting calculation...
An unhandled exception of type 'System.DivideByZeroException' occurred...

Stack Trace Example

When an exception occurs, the call stack shows the execution path:

at Program.Main() in C:\Code\Program.cs:line 10
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)

Visualization:

flowchart TD
    A["Main()"] --> B["Calculate()"]
    B --> C["Divide(10, 0)"] --> D["DivideByZeroException"]

Key Takeaways:

  • The stack trace points to Divide(10, 0) as the root cause.
  • Use Debug.WriteLine to log intermediate values during development.

4. Choosing the Right Collection

Scenario Recommended Collection Why?
Need indexed access List<T> O(1) random access
Fast lookups by key Dictionary<TKey,TValue> O(1) average case
Process items in order Queue<T> FIFO (e.g., task scheduling)
Undo/redo operations Stack<T> LIFO (e.g., navigation history)
Unique elements only HashSet<T> O(1) add/contains

5. Common Pitfalls and Best Practices

  • Avoid catch (Exception) without logging: Swallowing exceptions hides bugs.
    // Bad: Silently ignores errors
    catch (Exception) { }
    
    // Good: Logs the error
    catch (Exception ex) {
        Debug.WriteLine($"Error: {ex.Message}\nStack Trace: {ex.StackTrace}");
    }
    
  • Use using for disposable resources (e.g., FileStream):
    using (FileStream fs = new FileStream("data.txt", FileMode.Open))
    {
        // Automatically closes the file
    }
    
  • Prefer TryGetValue for dictionaries to avoid KeyNotFoundException:
    if (scores.TryGetValue("Alice", out int score)) {
        Console.WriteLine(score);
    }
    

In the Real World

  1. eSewa (Nepal)

    • Idea Used: Dictionary<string, User>
    • How: Maps user IDs (e.g., "USER123") to User objects for O(1) authentication during login.
    • Why It Matters: Handles thousands of transactions per second without performance lag.
  2. Daraz (Nepal)

    • Idea Used: List<Order> and Queue<Order>
    • How:
      • List<Order> stores all orders for a user (indexed by order ID).
      • Queue<Order> processes pending deliveries in FIFO order (first order placed = first delivered).
    • Why It Matters: Ensures fair delivery scheduling and easy order tracking.
  3. Pathao (Nepal)

    • Idea Used: Stack<Route>
    • How: Implements "undo" for navigation history. Drivers can pop the last route to revert changes.
    • Why It Matters: Improves user experience by allowing corrections without restarting the trip.
  4. NTC Call Center (Nepal)

    • Idea Used: Queue<Customer>
    • How: Customers are enqueued when they call, and the next in line is dequeued for service.
    • Why It Matters: Prevents chaos in high-call-volume scenarios (e.g., during outages).
  5. NEPSE (Nepal Stock Exchange)

    • Idea Used: HashSet<string>
    • How: Stores unique stock symbols (e.g., "NTC", "NBL") to avoid duplicates.
    • Why It Matters: Ensures no duplicate trades or invalid symbols are processed.

Exam Tip

  1. Exception Handling Questions (20-30% weight)

    • What to expect:
      • Write try-catch-finally blocks for given scenarios (e.g., file I/O, division).
      • Identify exceptions thrown by specific code snippets.
    • Common Patterns:
      • Always include a finally block for cleanup (e.g., closing files).
      • Use specific exception types (e.g., catch (FileNotFoundException)) instead of generic catch (Exception).
  2. Collections Questions (30-40% weight)

    • What to expect:
      • Choose the right collection for a scenario (e.g., "Use a Queue for task scheduling").
      • Write code to add/retrieve items from collections.
      • Explain time complexities (e.g., "Why is Dictionary lookup O(1)?").
    • Common Pitfalls:
      • Confusing List<T> (indexed) with Dictionary<TKey,TValue> (key-based).
      • Forgetting that List<T>.Insert() is O(n) in the middle.
  3. Debugging Questions (10-20% weight)

    • What to expect:
      • Interpret stack traces to identify errors.
      • Use Debug.WriteLine to trace variable values.
    • Pro Tip: Always include debug statements in your exam code to show you understand the flow.
  4. Real-World Application (10% weight)

    • What to expect:
      • Relate collections/exceptions to Nepalese software (e.g., "How would you handle a failed payment in eSewa?").
    • How to Answer:
      • Start with the collection/exception type.
      • Explain the real-world use case (e.g., "Use a Dictionary to store user sessions for fast lookup").
      • Mention performance benefits (e.g., "O(1) lookup ensures quick authentication").

Final Checklist Before the Exam:

  • Can you list 3 exceptions and their causes?
  • Can you write a try-catch-finally block for file reading?
  • Do you know when to use List<T>, Dictionary<TKey,TValue>, Queue<T>, or Stack<T>?
  • Can you trace the execution of a collection operation (e.g., Insert in List<T>)?
  • Can you interpret a stack trace to find an error?

Based on the TU BITM syllabus for .NET Programming (IT275), unit 4.

Discussion

Loading…