.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) usingtry-catch-finallyblocks. - 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, whileDictionary<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).
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).
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)).
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)).
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.WriteLineto 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
usingfor disposable resources (e.g.,FileStream):using (FileStream fs = new FileStream("data.txt", FileMode.Open)) { // Automatically closes the file } - Prefer
TryGetValuefor dictionaries to avoidKeyNotFoundException:if (scores.TryGetValue("Alice", out int score)) { Console.WriteLine(score); }
In the Real World
eSewa (Nepal)
- Idea Used:
Dictionary<string, User> - How: Maps user IDs (e.g.,
"USER123") toUserobjects for O(1) authentication during login. - Why It Matters: Handles thousands of transactions per second without performance lag.
- Idea Used:
Daraz (Nepal)
- Idea Used:
List<Order>andQueue<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.
- Idea Used:
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.
- Idea Used:
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).
- Idea Used:
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.
- Idea Used:
Exam Tip
Exception Handling Questions (20-30% weight)
- What to expect:
- Write
try-catch-finallyblocks for given scenarios (e.g., file I/O, division). - Identify exceptions thrown by specific code snippets.
- Write
- Common Patterns:
- Always include a
finallyblock for cleanup (e.g., closing files). - Use specific exception types (e.g.,
catch (FileNotFoundException)) instead of genericcatch (Exception).
- Always include a
- What to expect:
Collections Questions (30-40% weight)
- What to expect:
- Choose the right collection for a scenario (e.g., "Use a
Queuefor task scheduling"). - Write code to add/retrieve items from collections.
- Explain time complexities (e.g., "Why is
Dictionarylookup O(1)?").
- Choose the right collection for a scenario (e.g., "Use a
- Common Pitfalls:
- Confusing
List<T>(indexed) withDictionary<TKey,TValue>(key-based). - Forgetting that
List<T>.Insert()is O(n) in the middle.
- Confusing
- What to expect:
Debugging Questions (10-20% weight)
- What to expect:
- Interpret stack traces to identify errors.
- Use
Debug.WriteLineto trace variable values.
- Pro Tip: Always include debug statements in your exam code to show you understand the flow.
- What to expect:
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
Dictionaryto store user sessions for fast lookup"). - Mention performance benefits (e.g., "O(1) lookup ensures quick authentication").
- What to expect:
Final Checklist Before the Exam:
- Can you list 3 exceptions and their causes?
- Can you write a
try-catch-finallyblock for file reading? - Do you know when to use
List<T>,Dictionary<TKey,TValue>,Queue<T>, orStack<T>? - Can you trace the execution of a collection operation (e.g.,
InsertinList<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…