Back to Home

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?

GoalUseWhat happens when the key already exists?
Add and reject duplicatesAdd(key, value)Throws ArgumentException
Add or overwritedict[key] = valueReplaces the existing value
Add only when absentTryAdd(key, value)Returns false and keeps the existing value
Find a value safelyTryGetValue(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

C# Example Code
var scores = new Dictionary<string, int>();

scores.Add("Alice", 95);        // add
scores["Bob"] = 87;             // add or overwrite
scores["Alice"] = 98;           // update existing value

Use 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

C# Example Code
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

C# Example Code
// 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:

C# Example Code
scores["Alice"] = 98;   // update existing
scores["Dave"]  = 76;   // add new

TryGetValue — Safe Lookup Without Exceptions

Avoid double-lookup by combining existence check and retrieval in one call.

C# Example Code
// 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

C# Example Code
Console.WriteLine(scores.ContainsKey("Bob"));    // True
Console.WriteLine(scores.ContainsKey("Eve"));    // False

// ContainsValue is O(n) — scans all values
Console.WriteLine(scores.ContainsValue(87));     // True

Remove Entries

C# Example Code
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}"); // 87

Iterating a Dictionary

C# Example Code
// 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

C# Example Code
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");        // 0

TryAdd — Add Only If Key Is Absent

C# Example Code
bool added = scores.TryAdd("Frank", 81);  // True — added
bool dup   = scores.TryAdd("Alice", 55);  // False — Alice already exists, value unchanged
Console.WriteLine(scores["Alice"]);        // 98

Common Patterns

Counting Occurrences

C# Example Code
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: 1

Grouping Items by a Key

C# Example Code
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

C# Example Code
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

C# Example Code
// 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

OperationCode
Add (throws if duplicate)dict.Add(key, value)
Add or overwritedict[key] = value
Safe add (no overwrite)dict.TryAdd(key, value)
Safe lookupdict.TryGetValue(key, out var val)
Lookup with fallbackdict.GetValueOrDefault(key, fallback)
Key exists?dict.ContainsKey(key)
Remove entrydict.Remove(key)
Remove + retrievedict.Remove(key, out var val)
Iterate pairsforeach (var (k, v) in dict)
Count entriesdict.Count
Empty the dictionarydict.Clear()

Take It Further

C# 12 and .NET 8 book cover

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.