Back to Blog
C#

C# Dictionary Key-Value Iteration: Methods and Pitfalls

Understand C# Dictionary key-value iteration with foreach, LINQ, and safe modification patterns, including ordering and performance considerations.

DictionaryIterationKeyValuePairLINQPerformanceforeach
A C# code editor showing a foreach loop iterating over a dictionary's key-value pairs.

In C#, the standard way to iterate over the entries in a Dictionary<TKey,TValue> is a foreach loop. Because the dictionary implements IEnumerable<KeyValuePair<TKey,TValue>>, each iteration yields a KeyValuePair<TKey,TValue> struct whose Key and Value properties give you both parts of the entry without an extra lookup. This basic pattern is the foundation for the other iteration techniques covered below.

Using foreach to Enumerate Entries

The simplest way to iterate dictionary entries is a foreach loop over the dictionary itself.

var settings = new Dictionary<string, string> { ["host"] = "localhost", ["port"] = "8080", ["timeout"] = "30" }; foreach (KeyValuePair<string, string> entry in settings) { Console.WriteLine($"{entry.Key}: {entry.Value}"); }

Dictionary<TKey,TValue> implements IEnumerable<KeyValuePair<TKey,TValue>>, so the compiler turns this foreach into a call to GetEnumerator(). The returned enumerator is a struct, so this loop does not allocate on the heap. The iteration order is not guaranteed; it follows the dictionary's internal bucket layout and can change when items are added or removed. If you need sorted order, use SortedDictionary<TKey,TValue> or sort the entries before iterating. If you need insertion order, keep a separate list of keys or use a collection that explicitly preserves it.

Working with KeyValuePair Directly

Each entry is a KeyValuePair<TKey,TValue> struct, and its Key and Value properties are read-only. You cannot modify a dictionary entry by assigning to those properties. Use the indexer to replace the value for a known key, and use TryGetValue when you need to read a value without throwing.

foreach (var entry in inventory) { // This does NOT update the dictionary. // entry.Value = entry.Value * 2; // Compile-time error: read-only property }

A KeyValuePair is a snapshot of one entry at the moment it is enumerated. It is convenient when you want to pass both parts to a method or store them in another collection.

Using LINQ to Transform or Filter Entries

LINQ is useful when you need to filter or project dictionary entries. You can combine Where, Select, and ToDictionary to build a new dictionary or extract a list.

var filtered = settings .Where(entry => entry.Value.Length > 3) .ToDictionary(entry => entry.Key, entry => entry.Value); var keysOnly = settings.Select(entry => entry.Key).ToList();

Where and Select are lazy: each time you enumerate the result, the dictionary is enumerated again. If you need a snapshot or plan to iterate the result more than once, materialize it with ToList() or ToDictionary().

Modifying a Dictionary While Iterating

Adding or removing entries inside a foreach loop throws InvalidOperationException with a message like Collection was modified; enumeration operation may not execute. The dictionary tracks a version number, and the enumerator checks it on each MoveNext call.

// This throws InvalidOperationException foreach (var entry in settings) { if (entry.Key == "host") { settings.Remove(entry.Key); // Throws } }

To remove or add entries, iterate over a snapshot of keys or entries. A common pattern is to collect the keys to remove first, then remove them after the loop.

var keysToRemove = settings.Keys .Where(key => key.StartsWith("temp")) .ToList(); foreach (var key in keysToRemove) { settings.Remove(key); }

Updating an existing value with the indexer is different: it does not change the dictionary's structure, so it is safe inside a foreach loop.

foreach (var entry in settings) { settings[entry.Key] = entry.Value.Trim(); }

Performance Considerations

Iterating with foreach is generally fast because the dictionary enumerator is a struct and does not allocate on the heap. Reading entry.Key and entry.Value does not perform an extra hash lookup, so it is cheaper than using dictionary[key] inside the loop.

If you only need the values, iterate dictionary.Values directly. If you only need the keys, iterate dictionary.Keys. This avoids copying the unneeded KeyValuePair into the loop variable.

LINQ operators add a small amount of overhead for delegate calls and iterator state machines. For most applications this is negligible, but in tight loops over very large dictionaries a plain foreach can be faster than an equivalent LINQ query. When you need a snapshot to iterate more than once, materialize it first.

Iteration order is another factor. It is not deterministic, and it can change when the dictionary is resized. If you need a stable order, sort the keys or entries before iterating; sorting adds O(n log n) work.

Choosing the Right Iteration Approach

ScenarioRecommended Approach
Read all key-value pairsforeach over the dictionary
Read only keysforeach over Keys
Read only valuesforeach over Values
Filter and create a new dictionaryLINQ Where + ToDictionary
Remove entries during iterationCollect keys or entries first, then remove
Update existing values during iterationUse the indexer with entry.Key

These choices balance readability, safety, and performance. In most code, the simplest approach creates the fewest surprises. Use LINQ when the transformation is complex or you need to chain multiple operations.

Handling Order and Dependencies

Because Dictionary<TKey,TValue> does not guarantee iteration order, do not write code that depends on entry order. If your logic requires sorted order, sort the keys first and then look up each value.

foreach (var key in settings.Keys.OrderBy(k => k)) { Console.WriteLine($"{key}: {settings[key]}"); }

This adds O(n log n) sorting plus O(n) lookups. If you frequently need sorted iteration and updates are less common, SortedDictionary<TKey,TValue> may be a better fit, although insertions and removals are slower.

Thread safety is another dependency. Dictionary<TKey,TValue> is not safe for concurrent reads and writes. If one thread iterates while another modifies the dictionary, you can receive an InvalidOperationException or read inconsistent data. ConcurrentDictionary<TKey,TValue> is the safer choice for concurrent access. Its enumerator can be used while the dictionary is modified, but it does not necessarily provide a moment-in-time snapshot, so entries added or removed during enumeration may or may not be included.

Advanced: Avoiding Unnecessary Copies

The foreach loop over a dictionary does not allocate, but the loop variable is a copy of each KeyValuePair. That copy is cheap because the struct contains only the key and value. If you pass the entry to a method that accepts an in parameter, you can avoid an additional copy of that argument. This is a micro-optimization; use it only when profiling shows that the extra copy matters. If you need indexed access to entries, materialize them with ToArray() and use a for loop. That adds memory overhead, so use it only when you genuinely need an index.

How to Iterate Over a C# Dictionary: foreach, LINQ, and Safe Modification | RYUSLOG DEV