C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
181: Dictionary<TKey,TValue> Members In Depth
We've already established that a Dictionary<TKey, TValue> is basically a map that lets us find values quickly using a unique key. But in a real production environment, you rarely just Add() a few items and call it a day. You'll be constantly checking for existence, updating values, and cleaning up old data. Let's build a small "Game Inventory" system to see how these members actually behave when things get messy.
Setting up our item stash
I'll start by creating a dictionary where the key is the item name (a string) and the value is the quantity (an int). I'm using a dictionary here because looking up an item by name in a list would require looping through every single entry, which is a waste of cycles as the inventory grows.
var inventory = new Dictionary<string, int>();
inventory.Add("Health Potion", 5);
inventory.Add("Mana Potion", 3);
inventory.Add("Iron Ore", 20);
Now, Add() is strict. If I tried to Add("Health Potion", 1) again, C# would throw an ArgumentException because the key already exists. In most real-world scenarios, I prefer using the indexer [] because it's more flexible—it adds the key if it's missing, or updates it if it's already there.
The "crash and burn" approach to updates
Let's say the player picks up a "Gold Coin". Since I don't know if the player already has coins in their inventory, I might try to do something like this:
// I'm assuming the key exists here...
inventory["Gold Coin"] += 10;
I've made a classic mistake here. Because "Gold Coin" isn't in the dictionary yet, the indexer doesn't know what value to add 10 to. This will throw a KeyNotFoundException and crash the game. I've fallen into the trap of assuming the state of the collection without verifying it first.
Safe retrieval with TryGetValue
I could use ContainsKey() to check if the item exists before updating it, but that actually forces the dictionary to search for the key twice: once to check if it's there, and once to retrieve the value. That's inefficient.
Instead, I'll use TryGetValue(). This is the "pro" way to handle dictionaries. It looks for the key once and, if found, assigns the value to an out variable.
string itemFound = "Gold Coin";
int amountFound = 10;
if (inventory.TryGetValue(itemFound, out int currentQuantity))
{
inventory[itemFound] = currentQuantity + amountFound;
}
else
{
inventory[itemFound] = amountFound;
}
See what happened there? I tried to get the value. If it existed, I added to it. If it didn't, I just initialized it. One lookup, no crashes.
Pruning and auditing the inventory
Eventually, the player will use items or drop them. We use Remove() for this. One thing I love about the modern Remove() method is that it also supports an out parameter if you want to know what the value was right before it was deleted.
if (inventory.Remove("Iron Ore", out int removedAmount))
{
Console.WriteLine($"Dropped {removedAmount} Iron Ore.");
}
Finally, if we need to see everything the player owns, we can access the Keys and Values collections. These aren't copies of the data; they are live views. If I change a value in the dictionary, it's reflected when I iterate through Values.
Console.WriteLine("Current Items:");
foreach (var item in inventory.Keys)
{
Console.WriteLine($"- {item}");
}
If I wanted to wipe the whole inventory for a "Game Over" screen, I wouldn't recreate the dictionary object; I'd just call inventory.Clear(). It keeps the capacity allocated, which is slightly better for performance if we're about to fill it up again.
📋 Practical Task
Exercise: Building a Player Skill Registry
Create a program that manages a player's skill levels using a Dictionary<string, int>. Your program must implement the following logic:
- Initialize the dictionary with three skills: "Strength" (1), "Agility" (1), and "Intelligence" (1).
- Create a method called
LevelUpSkill(string skillName). - Inside that method, use
TryGetValueto check if the skill exists. - If the skill exists, increment its value by 1.
- If the skill does not exist, add it to the dictionary with an initial value of 1.
- After performing a few level-ups (including one for a skill that didn't exist initially), use a
foreachloop to print all skill names and their levels to the console. - Finally, remove the "Agility" skill from the registry and print a confirmation message including the level that was removed.
There are no comments for now.