C# Dictionary: Add, Update, TryGetValue, TryAdd, and Iterate
Dictionary<TKey, TValue> is the go-to collection for fast key-based lookups. If you are asking "what is a dictionary in C#?", the short answer is: it stores data as key/value pairs so you can look up values quickly by key.
Quick answer: use a Dictionary when you need fast lookups by a unique key, such as user IDs, configuration values, or word counts.
Which Dictionary Operation Should You Use?
| Goal | Use | What happens when the key already exists? |
|---|---|---|
| Add and reject duplicates | Add(key, value) | Throws ArgumentException |
| Add or overwrite | dict[key] = value | Replaces the existing value |
| Add only when absent | TryAdd(key, value) | Returns false and keeps the existing value |
| Find a value safely | TryGetValue(key, out value) | Returns false when the key is missing |
For most lookups, prefer TryGetValue because it checks the key and retrieves the value in one operation. For input that may contain an existing key, choose TryAdd or indexer assignment based on whether overwriting is allowed.
Add or Update Entries
var scores = new Dictionary<string, int>();
scores.Add("Alice", 95); // add
scores["Bob"] = 87; // add or overwrite
scores["Alice"] = 98; // update existing valueUse Add when you want to throw if the key already exists. Use the indexer dict[key] = value when you want "add or overwrite" behavior.
Iterate a Dictionary with foreach
foreach (var (name, score) in scores)
Console.WriteLine($"{name}: {score}");This is the most direct way to loop over a dictionary when you want both keys and values.
Create and Add Entries
// Create with initial capacity (avoids resizing when size is known)
var scores = new Dictionary<string, int>();
// Add entries
scores.Add("Alice", 95);
scores.Add("Bob", 87);
scores.Add("Carol", 92);
// Collection initializer syntax
var config = new Dictionary<string, string>
{
["host"] = "localhost",
["port"] = "5432",
["database"] = "mydb"
};Add throws ArgumentException if the key already exists. Use the indexer [key] = value to add or overwrite:
scores["Alice"] = 98; // update existing
scores["Dave"] = 76; // add newTryGetValue — Safe Lookup Without Exceptions
Avoid double-lookup by combining existence check and retrieval in one call.
// BAD — checks twice (ContainsKey + indexer)
if (scores.ContainsKey("Alice"))
{
int s = scores["Alice"]; // ❌ second hash lookup
}
// GOOD — single lookup
if (scores.TryGetValue("Alice", out int score))
{
Console.WriteLine($"Alice: {score}"); // Alice: 98
}
else
{
Console.WriteLine("Not found");
}ContainsKey and ContainsValue
Console.WriteLine(scores.ContainsKey("Bob")); // True
Console.WriteLine(scores.ContainsKey("Eve")); // False
// ContainsValue is O(n) — scans all values
Console.WriteLine(scores.ContainsValue(87)); // TrueRemove Entries
bool removed = scores.Remove("Dave");
Console.WriteLine(removed); // True
// Remove and retrieve the value in one call (.NET 5+)
if (scores.Remove("Bob", out int bobScore))
Console.WriteLine($"Removed Bob with score {bobScore}"); // 87Iterating a Dictionary
// Iterate key-value pairs (order is not guaranteed)
foreach (KeyValuePair<string, int> pair in scores)
Console.WriteLine($"{pair.Key}: {pair.Value}");
// Deconstruct the pair (C# 7+)
foreach (var (name, value) in scores)
Console.WriteLine($"{name}: {value}");
// Keys only
foreach (string key in scores.Keys)
Console.WriteLine(key);
// Values only
foreach (int val in scores.Values)
Console.WriteLine(val);GetValueOrDefault — Fallback Without Try/Catch
int aliceScore = scores.GetValueOrDefault("Alice", 0); // 98
int eveScore = scores.GetValueOrDefault("Eve", 0); // 0 (default)
// Without a default, returns the type default (0 for int, null for reference types)
int missing = scores.GetValueOrDefault("Nobody"); // 0TryAdd — Add Only If Key Is Absent
bool added = scores.TryAdd("Frank", 81); // True — added
bool dup = scores.TryAdd("Alice", 55); // False — Alice already exists, value unchanged
Console.WriteLine(scores["Alice"]); // 98Common Patterns
Counting Occurrences
string[] words = { "apple", "banana", "apple", "cherry", "banana", "apple" };
var counts = new Dictionary<string, int>();
foreach (string word in words)
{
counts.TryGetValue(word, out int current);
counts[word] = current + 1;
}
// Shorter with GetValueOrDefault
foreach (string word in words)
counts[word] = counts.GetValueOrDefault(word) + 1;
foreach (var (word, count) in counts)
Console.WriteLine($"{word}: {count}");
// apple: 3, banana: 2, cherry: 1Grouping Items by a Key
var people = new[]
{
new { Name = "Alice", Dept = "Engineering" },
new { Name = "Bob", Dept = "Marketing" },
new { Name = "Carol", Dept = "Engineering" },
new { Name = "Dave", Dept = "Marketing" },
};
var byDept = new Dictionary<string, List<string>>();
foreach (var person in people)
{
if (!byDept.ContainsKey(person.Dept))
byDept[person.Dept] = new List<string>();
byDept[person.Dept].Add(person.Name);
}
// With LINQ — same result, more concise
var byDeptLinq = people
.GroupBy(p => p.Dept)
.ToDictionary(g => g.Key, g => g.Select(p => p.Name).ToList());Lookup Table / Dispatcher
var handlers = new Dictionary<string, Action<string>>
{
["greet"] = name => Console.WriteLine($"Hello, {name}!"),
["shout"] = name => Console.WriteLine(name.ToUpper()),
["reverse"] = name => Console.WriteLine(new string(name.Reverse().ToArray())),
};
string command = "greet";
string arg = "World";
if (handlers.TryGetValue(command, out var handler))
handler(arg); // Hello, World!
else
Console.WriteLine($"Unknown command: {command}");Nested Dictionaries
// Matrix: row key → column key → value
var matrix = new Dictionary<string, Dictionary<string, double>>
{
["Alice"] = new() { ["Math"] = 95.0, ["Science"] = 88.5 },
["Bob"] = new() { ["Math"] = 72.0, ["Science"] = 91.0 },
};
if (matrix.TryGetValue("Alice", out var row) &&
row.TryGetValue("Math", out double mathScore))
{
Console.WriteLine($"Alice Math: {mathScore}"); // 95
}FAQ
What is the difference between Add and the indexer?
Use Add when you want to reject duplicate keys. Use dict[key] = value when you want to insert a new key or replace an existing one.
When should I use TryGetValue instead of ContainsKey + indexer?
Use TryGetValue when you want one lookup that both checks the key and retrieves the value without a second hash lookup.
Is Dictionary case-sensitive by default?
Yes. Dictionary keys are compared using the default equality comparer, which is case-sensitive for strings unless you create the dictionary with a case-insensitive comparer.
Quick Reference
| Operation | Code |
|---|---|
| Add (throws if duplicate) | dict.Add(key, value) |
| Add or overwrite | dict[key] = value |
| Safe add (no overwrite) | dict.TryAdd(key, value) |
| Safe lookup | dict.TryGetValue(key, out var val) |
| Lookup with fallback | dict.GetValueOrDefault(key, fallback) |
| Key exists? | dict.ContainsKey(key) |
| Remove entry | dict.Remove(key) |
| Remove + retrieve | dict.Remove(key, out var val) |
| Iterate pairs | foreach (var (k, v) in dict) |
| Count entries | dict.Count |
| Empty the dictionary | dict.Clear() |
Take It Further

The most comprehensive book covering C# 12 and .NET 8 from scratch to production.
C# 12 and .NET 8 — Modern Cross-Platform Development · Mark J. Price
Get it on Amazon →As an Amazon Associate I earn from qualifying purchases.