C# Programming Labs

Master dictionaries, file operations, and event-driven programming in C#.

Dictionaries & File I/O - Module 5

Learn key-value collections, file operations, and delegates/events.

Lab 13: Dictionary & Key-Value Collections
Advanced
Coding Challenge
Your Task: Work with Dictionary<TKey, TValue> to store and retrieve data using key-value pairs.

Detailed Requirements:
1. Create and initialize a Dictionary:
using System.Collections.Generic; // Create empty dictionary Dictionary<string, int> ages = new Dictionary<string, int>(); // Or initialize with values Dictionary<string, int> ages = new Dictionary<string, int> { { "Alice", 25 }, { "Bob", 30 }, { "Charlie", 35 } };

2. Add and access elements:
// Add new entry ages["Diana"] = 28; ages.Add("Eve", 22); // Throws if key exists // Access by key int aliceAge = ages["Alice"]; // 25 // Safe access with TryGetValue if (ages.TryGetValue("Frank", out int frankAge)) { Console.WriteLine($"Frank is {frankAge}"); } else { Console.WriteLine("Frank not found"); }

3. Check and remove entries:
// Check if key exists bool hasAlice = ages.ContainsKey("Alice"); // true bool has25 = ages.ContainsValue(25); // true // Remove entry ages.Remove("Bob"); // Get count int count = ages.Count;

4. Iterate over dictionary:
// Iterate key-value pairs foreach (KeyValuePair<string, int> kvp in ages) { Console.WriteLine($"{kvp.Key}: {kvp.Value}"); } // Or use var for cleaner syntax foreach (var (name, age) in ages) { Console.WriteLine($"{name} is {age} years old"); } // Iterate just keys or values foreach (string name in ages.Keys) { } foreach (int age in ages.Values) { }

💡 Pro Tips:
• Keys must be unique; values can repeat
• Use TryGetValue instead of checking ContainsKey then accessing
• SortedDictionary keeps keys in sorted order
• For thread-safe access, use ConcurrentDictionary
• Initialize capacity if you know the size: new Dictionary<K,V>(100)

Expected Output:
Alice: 25 Bob: 30 Charlie: 35 Diana: 28 Count: 4 Contains Alice: True Bob's age: 30

Requirements Checklist

Create Dictionary<string, int>
Add entries using indexer or Add()
Access values by key
Use ContainsKey() or TryGetValue()
Iterate with foreach loop
Use .Count property
Output
// Click "Run Code" to compile and execute
Hints & Tips
• Create: Dictionary<string, int> dict = new Dictionary<string, int>();
• Add: dict["key"] = value; or dict.Add("key", value);
• Access: int val = dict["key"];
• Check: dict.ContainsKey("key")
• Iterate: foreach (var kvp in dict) { }
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

Lab 14: File I/O Operations
Advanced
Coding Challenge
Your Task: Learn to read from and write to files using System.IO classes.

Detailed Requirements:
1. Write text to a file:
using System.IO; // Write all text at once (creates or overwrites) File.WriteAllText("output.txt", "Hello, World!"); // Write multiple lines string[] lines = { "Line 1", "Line 2", "Line 3" }; File.WriteAllLines("output.txt", lines); // Append to existing file File.AppendAllText("output.txt", "\nAppended text");

2. Read text from a file:
// Read entire file as string string content = File.ReadAllText("input.txt"); Console.WriteLine(content); // Read as array of lines string[] lines = File.ReadAllLines("input.txt"); foreach (string line in lines) { Console.WriteLine(line); }

3. Check if file exists:
if (File.Exists("myfile.txt")) { Console.WriteLine("File exists!"); string content = File.ReadAllText("myfile.txt"); } else { Console.WriteLine("File not found."); }

4. Work with directories:
// Create directory Directory.CreateDirectory("MyFolder"); // Check if directory exists if (Directory.Exists("MyFolder")) { // Get all files in directory string[] files = Directory.GetFiles("MyFolder"); foreach (string file in files) { Console.WriteLine(file); } }

5. Use StreamWriter/StreamReader for large files:
// Writing with StreamWriter using (StreamWriter writer = new StreamWriter("log.txt")) { writer.WriteLine("Log entry 1"); writer.WriteLine("Log entry 2"); } // Reading with StreamReader using (StreamReader reader = new StreamReader("log.txt")) { string line; while ((line = reader.ReadLine()) != null) { Console.WriteLine(line); } }

💡 Pro Tips:
• Always use using statements for streams (auto-disposes)
• Use Path.Combine() for cross-platform paths
• Handle IOException and FileNotFoundException
• File.ReadAllTextAsync() for async operations
• Use FileInfo for file metadata (size, dates, etc.)

Expected Output:
Writing to file... File written successfully! Reading from file: Hello from C#! This is line 2. This is line 3. File exists: True

Requirements Checklist

Use File.WriteAllText() or WriteAllLines()
Use File.ReadAllText() or ReadAllLines()
Check file existence with File.Exists()
Use StreamWriter with using statement
Use StreamReader with using statement
Handle file path properly
Output
// Click "Run Code" to compile and execute
Hints & Tips
• Write: File.WriteAllText("file.txt", "content");
• Read: string content = File.ReadAllText("file.txt");
• Check: if (File.Exists("file.txt")) { }
• Stream: using (StreamWriter sw = new StreamWriter("f.txt")) { }
• Always add using System.IO; at the top
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

Lab 15: Delegates & Events
Expert
Coding Challenge
Your Task: Learn delegates (type-safe function pointers) and events for implementing the observer pattern.

Detailed Requirements:
1. Declare and use a delegate:
// Declare a delegate type delegate void MessageHandler(string message); // Create methods matching the signature static void PrintMessage(string msg) { Console.WriteLine($"Message: {msg}"); } static void LogMessage(string msg) { Console.WriteLine($"[LOG] {msg}"); } // Use the delegate MessageHandler handler = PrintMessage; handler("Hello!"); // Output: Message: Hello! // Multicast delegate (multiple methods) handler += LogMessage; handler("Test"); // Calls both methods!

2. Use built-in delegates (Action, Func):
// Action: void return, 0-16 parameters Action<string> print = msg => Console.WriteLine(msg); print("Hello"); // Func: has return value (last type param) Func<int, int, int> add = (a, b) => a + b; int result = add(5, 3); // 8 // Predicate: returns bool Predicate<int> isEven = n => n % 2 == 0; bool even = isEven(4); // true

3. Create and raise events:
class Button { // Declare event using EventHandler delegate public event EventHandler Clicked; // Method to raise the event public void OnClick() { Console.WriteLine("Button clicked!"); Clicked?.Invoke(this, EventArgs.Empty); } } // Subscribe to event Button btn = new Button(); btn.Clicked += (sender, e) => Console.WriteLine("Handler 1 called"); btn.Clicked += (sender, e) => Console.WriteLine("Handler 2 called"); btn.OnClick(); // Triggers all handlers

4. Custom EventArgs:
class MessageEventArgs : EventArgs { public string Message { get; set; } } class Publisher { public event EventHandler<MessageEventArgs> MessageReceived; public void SendMessage(string msg) { MessageReceived?.Invoke(this, new MessageEventArgs { Message = msg }); } }

💡 Pro Tips:
• Use Action and Func instead of custom delegates when possible
• Always use ?.Invoke() to safely raise events (null check)
• Events can only be invoked from within the class that declares them
• Use -= to unsubscribe from events (prevent memory leaks)
• Lambda expressions are concise event handlers

Expected Output:
Using delegate: Message: Hello from delegate! [LOG] Hello from delegate! Using Action and Func: Sum: 8 Button event: Button clicked! Handler executed!

Requirements Checklist

Declare a delegate type
Create method matching delegate signature
Invoke delegate with parameters
Use Action or Func delegate
Declare an event
Subscribe to and trigger event
Output
// Click "Run Code" to compile and execute
Hints & Tips
• Delegate: delegate void Handler(string msg);
• Assign: Handler h = MyMethod;
• Action: Action<string> a = s => Console.WriteLine(s);
• Func: Func<int, int> f = x => x * 2;
• Event: public event EventHandler MyEvent;
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below