.NET ProgrammingUnit 411 min read
Exception Handling & Collections in .NET: Errors, Lists, Dictionaries
Unit 4 of .NET Programming covers structured error handling (try-catch-finally) and .NET’s core collections (ArrayList, List<T>, Dictionary<TKey,TValue>, Queue<T>, Stack<T>, HashSet<T>), including their operations, performance trade-offs, and real-world use in data processing, caching, and transaction logging.
TAKEAWAYS:
- Exception handling uses
try-catch-finallyto gracefully manage runtime errors (e.g.,NullReferenceException,DivideByZeroException) and log them for debugging. - .NET collections are optimized for different access patterns:
List<T>for indexed access,Dictionary<TKey,TValue>for key-value lookups,Queue<T>for FIFO operations, andStack<T>for LIFO. - Generic collections (
List<T>,Dictionary<TKey,TValue>) enforce type safety at compile time, improving performance and reducing runtime errors. - LINQ with collections enables powerful querying (e.g.,
Where(),OrderBy()) directly on collection objects. - Thread-safe collections (
ConcurrentQueue<T>,BlockingCollection<T>) are critical for multi-threaded applications to avoid race conditions. - Memory vs. performance trade-offs dictate collection choice: arrays are fastest for fixed-size data, while
List<T>dynamically resizes but has overhead.
Exception Handling: Structured Error Management
Why Handle Exceptions?
Unchecked errors (e.g., file not found, invalid user input) crash applications. Exception handling isolates errors, logs them, and allows graceful recovery. In .NET, exceptions are objects derived from System.Exception.
Key Components
flowchart TD
A["try"] -->|"Error occurs"| B["catch (ExceptionType e)"]
A -->|"Always executes"| C["finally"]
B --> D["Log error\nRecover or rethrow"]
C --> DWorked Example: File Read with Validation
try
{
string[] lines = File.ReadAllLines("data.txt");
int sum = lines.Sum(line => int.Parse(line)); // May throw FormatException
Console.WriteLine($"Sum: {sum}");
}
catch (FileNotFoundException ex)
{
Console.WriteLine($"File missing: {ex.Message}");
// Retry or notify admin
}
catch (FormatException ex)
{
Console.WriteLine($"Invalid data format: {ex.Message}");
}
finally
{
Console.WriteLine("Operation attempted.");
}
Trace Table:
| Step | lines |
Exception Thrown | Output |
|---|---|---|---|
| 1 | null |
FileNotFoundException |
"File missing: data.txt" |
| 2 | ["10", "20"] |
None | "Sum: 30" |
| 3 | ["abc"] |
FormatException |
"Invalid data format: abc" |
Common Exceptions in .NET
| Exception Type | Cause | Example |
|---|---|---|
NullReferenceException |
Accessing a null object |
string.Length on null |
IndexOutOfRangeException |
Array/list index invalid | list[99] on 10-item list |
InvalidOperationException |
Invalid API usage | stack.Pop() on empty stack |
ArgumentNullException |
Passing null to a method |
string.IsNullOrEmpty(null) |
Best Practices
- Catch specific exceptions (avoid
catch (Exception)unless logging). - Use
finallyfor cleanup (e.g., closing files, releasing locks). - Log exceptions with
ex.ToString()orSerilog. - Avoid empty
catchblocks—at least log or rethrow.
Collections: Choosing the Right Data Structure
1. Arrays vs. Lists
Arrays are fixed-size, zero-based, and faster for access but inflexible. List<T> dynamically resizes (default capacity: 4, grows by 2x).
Performance Comparison:
| Operation | Array (int[]) |
List<T> (List<int>) |
|---|---|---|
| Access by index | O(1) | O(1) |
| Insert at end | O(1)* | O(1) |
| Insert at start | O(n) | O(n) |
| Remove at end | O(1) | O(1) |
| Memory overhead | Low | High (object headers) |
*If no resize needed.
2. Dictionaries: Key-Value Lookups
Dictionary<TKey,TValue> uses a hash table for O(1) average-case lookups. Collisions are resolved via chaining (linked lists).
Worked Example: Caching User Sessions
var userSessions = new Dictionary<string, DateTime>();
userSessions["user123"] = DateTime.Now; // Hash: "user123" → Bucket 0
userSessions["admin"] = DateTime.Now.AddHours(-1); // Hash: "admin" → Bucket 1
Trace: Adding and Looking Up
| Step | userSessions |
Action | Output/State |
|---|---|---|---|
| Initial | {} |
Add("user123", now) |
Bucket 0: ["user123" → now] |
| After collision | Bucket 1: ["admin" → oldTime] |
Add("admin", now) |
Bucket 1: ["admin" → now] |
| Lookup "user123" | Bucket 0: ["user123" → now] |
userSessions["user123"] |
Returns now (O(1)) |
3. Queues and Stacks: FIFO vs. LIFO
- Queue<T>: First-In-First-Out (e.g., task scheduling, breadth-first search).
- Stack<T>: Last-In-First-Out (e.g., undo operations, expression evaluation).
Worked Example: Pathao Order Queue
var orderQueue = new Queue<string>();
orderQueue.Enqueue("Order123"); // FIFO: first to be processed
orderQueue.Enqueue("Order124");
Console.WriteLine(orderQueue.Dequeue()); // "Order123"
Trace:
| Step | orderQueue |
Operation | Output |
|---|---|---|---|
| 1 | ["Order123"] |
Enqueue("Order124") |
["Order123", "Order124"] |
| 2 | ["Order124"] |
Dequeue() |
"Order123" |
4. HashSet<T>: Unique Elements
HashSet<T> ensures uniqueness via hashing. Useful for deduplication (e.g., removing duplicate emails).
var uniqueEmails = new HashSet<string> { "a@b.com", "c@d.com" };
uniqueEmails.Add("a@b.com"); // Ignored (already exists)
Console.WriteLine(uniqueEmails.Count); // 2
5. Thread-Safe Collections
For multi-threaded apps, use:
ConcurrentQueue<T>: Thread-safe queue.BlockingCollection<T>: Blocks when empty/full (e.g., producer-consumer patterns).
var safeQueue = new ConcurrentQueue<int>();
safeQueue.Enqueue(1); // Thread-safe
In the Real World
eSewa (Nepal):
- Collection Used:
Dictionary<string, Transaction>to map transaction IDs to user data. - Why? O(1) lookup for verifying payments (e.g.,
transactions["TXN123"].Status). - Exception Handling: Catches
PaymentFailedExceptionto retry or notify users.
- Collection Used:
Khalti (Nepal):
- Collection Used:
Queue<PaymentRequest>for processing payments in FIFO order. - Why? Ensures fairness and prevents starvation (e.g.,
paymentQueue.Dequeue()).
- Collection Used:
Daraz (Nepal):
- Collection Used:
List<Order>for order management +HashSet<string>to track unique SKUs. - Why?
HashSetavoids duplicate inventory counts;Listmaintains order for processing.
- Collection Used:
NTC (Nepal Telecom):
- Exception Handling: Logs
NetworkUnavailableExceptionwhen SIM registration fails, then retries.
- Exception Handling: Logs
Bank Loan Calculations (Global):
- Collection Used:
Dictionary<int, Loan>whereintis account number. - Worked Example: Calculate monthly interest for account
1001:var loans = new Dictionary<int, Loan> { { 1001, new Loan(100000, 0.05) } }; try { double monthlyInterest = loans[1001].CalculateMonthlyInterest(); Console.WriteLine(monthlyInterest); // 416.67 } catch (KeyNotFoundException) { Console.WriteLine("Account not found."); }
- Collection Used:
LINQ with Collections: Querying Data
LINQ (Language Integrated Query) extends collections with SQL-like operations.
var numbers = new List<int> { 1, 5, 3, 8, 2 };
var evens = numbers.Where(n => n % 2 == 0).OrderBy(n => n);
Console.WriteLine(string.Join(", ", evens)); // "2, 8"
Common LINQ Methods:
| Method | Purpose | Example |
|---|---|---|
Where() |
Filter elements | .Where(x => x > 5) |
Select() |
Project to new type | .Select(x => x * 2) |
OrderBy() |
Sort ascending | .OrderBy(x => x.Name) |
GroupBy() |
Group by key | .GroupBy(x => x.Category) |
FirstOrDefault() |
Get first or default | .FirstOrDefault() |
Performance Considerations
| Collection | Best For | Worst For | Time Complexity (Avg) |
|---|---|---|---|
List<T> |
Frequent access by index | Frequent insertions | O(1) access, O(n) insert at start |
Dictionary<TKey,TValue> |
Key-value lookups | Many collisions | O(1) lookup, O(n) resize |
Queue<T> |
FIFO operations | Random access | O(1) enqueue/dequeue |
Stack<T> |
LIFO operations | Sequential access | O(1) push/pop |
HashSet<T> |
Uniqueness checks | Order matters | O(1) add/contains |
Exam Tip
Exception Handling:
- Must-know:
try-catch-finallysyntax,usingforIDisposable(e.g.,FileStream). - Common pitfalls: Swallowing exceptions (
catch (Exception)without logging), not cleaning up infinally. - Exam question type: Given a code snippet with potential errors, identify the exception thrown and fix it.
- Must-know:
Collections:
- Must-know: When to use
List<T>vs.Dictionary<TKey,TValue>vs.Queue<T>. - Performance trade-offs: Memorize O(1) vs. O(n) operations for each collection.
- Exam question type:
- "Which collection would you use for [scenario] and why?"
- "Trace the state of a
Dictionaryafter a series ofAdd/Removeoperations." - "Rewrite this array-based solution using
List<T>."
- Must-know: When to use
LINQ:
- Must-know:
Where(),Select(),OrderBy(),GroupBy(). - Exam question type: Given a collection, write a LINQ query to filter/sort/group data.
- Must-know:
Real-world scenarios:
- Expect questions like:
- "How would you implement a user session cache using
Dictionary?" - "Design a thread-safe order processing system using
BlockingCollection."
- "How would you implement a user session cache using
- Expect questions like:
Summary Checklist
Before the exam, verify you can:
- Write
try-catch-finallyblocks for common exceptions. - Choose the right collection for a given scenario (e.g.,
Queuefor task scheduling). - Trace the state of a collection after operations (e.g.,
Dictionaryafter collisions). - Use LINQ to query collections (e.g., filter and sort).
- Explain the trade-offs between arrays and
List<T>. - Describe how
Dictionaryhandles collisions (hashing + chaining).
Based on the TU BIM syllabus for .NET Programming (IT275), unit 4.
Discussion
Loading…