Back to Blog
C#

C# Dictionary foreach: Iteration Patterns and Pitfalls

Learn how C# Dictionary foreach works: iterate key-value pairs, keys, or values, and avoid common pitfalls such as modifying a dictionary during enumeration.

C#DictionaryforeachKeyValuePairEnumerationPerformance
Illustration of a hand holding a key while a loop arrow passes over a dictionary, representing C# Dictionary foreach iteration.

Using foreach with a C# Dictionary<TKey, TValue> is a common way to read all entries, keys, or values. The loop variable for the dictionary itself is a KeyValuePair<TKey, TValue> struct with Key and Value properties. The main limitations to remember are that iteration order is not guaranteed and that modifying the dictionary's structure during enumeration throws an InvalidOperationException. The examples below show the common iteration patterns and the safe ways to handle modifications.

The Basic foreach Over a Dictionary

The simplest form of iterating a dictionary is a foreach loop over the dictionary itself. Each iteration gives you a KeyValuePair<TKey, TValue> value, with Key and Value properties.

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}"); }

This pattern works for any dictionary type. The order of iteration is not guaranteed by the .NET runtime. If you need deterministic ordering, use a SortedDictionary or sort the keys explicitly.

Because KeyValuePair<TKey, TValue> is a struct, the loop variable is a copy of the pair. The copy overhead is normally negligible, but it is worth knowing if you are iterating over a very large collection in a hot path.

Iterating Keys and Values Separately

Sometimes you only need the keys or only the values. The Dictionary<TKey, TValue> class exposes Keys and Values properties that return dedicated collections. These are not snapshots; they are views over the underlying dictionary. Iterating them with foreach behaves similarly to iterating the dictionary itself.

foreach (string key in settings.Keys) { Console.WriteLine($"Key: {key}"); } foreach (string value in settings.Values) { Console.WriteLine($"Value: {value}"); }

Using Keys or Values is slightly more efficient than iterating the full KeyValuePair when you only need one side, because the enumerator does not need to expose the other side. The difference is usually minor unless you are processing millions of entries. The main benefit is readability: the code clearly communicates that you are interested only in keys or only in values.

Why Modifying a Dictionary During foreach Throws

A common mistake is attempting to add or remove entries while iterating with foreach. The .NET collection enumerators for dictionaries are fail-fast by design. When the dictionary detects that its version has changed, the next MoveNext() call throws an InvalidOperationException with the message "Collection was modified; enumeration operation may not execute."

var numbers = new Dictionary<int, string> { [1] = "one", [2] = "two" }; foreach (var entry in numbers) { numbers.Remove(entry.Key); // Throws InvalidOperationException }

This behavior is intentional. Allowing modification during enumeration would make the iteration order unpredictable and could lead to skipped entries or infinite loops. The exception protects you from subtle bugs that would be difficult to diagnose.

The same restriction applies to the Keys and Values collections. Any structural change—adding, removing, or clearing entries—invalidates the enumerator. Simply updating the value of an existing key does not invalidate the enumerator because the dictionary's internal structure remains unchanged.

Safe Ways to Modify a Dictionary While Iterating

If you need to remove entries that match a condition, you have two reliable options. The first is to collect the keys you want to remove in a separate list, then iterate that list and remove from the dictionary after the enumeration has completed.

var toRemove = new List<int>(); foreach (var entry in numbers) { if (entry.Value.StartsWith("t")) { toRemove.Add(entry.Key); } } foreach (int key in toRemove) { numbers.Remove(key); }

This works because the removal loop is not iterating the dictionary itself. The second option is to use LINQ to build a new dictionary that excludes the unwanted entries. This is often more concise and avoids the two-step process.

var filtered = numbers.Where(entry => !entry.Value.StartsWith("t")) .ToDictionary(entry => entry.Key, entry => entry.Value);

Be aware that this creates a completely new dictionary, which has memory and CPU costs. For small dictionaries it is fine, but for large ones the copy may be noticeable. The two-step removal approach modifies the original dictionary in place and is generally more memory efficient.

Performance Characteristics of Dictionary Enumeration

Enumeration of a Dictionary<TKey, TValue> in the standard .NET implementation walks an internal array of entries and skips slots that are not in use. This means the time to iterate is proportional to the number of entries plus the number of empty slots in the internal array. After many removals, the dictionary may contain a significant number of empty slots, making enumeration slower than the entry count suggests.

If you are repeatedly iterating a dictionary that is frequently modified, consider calling TrimExcess() after a batch of removals on runtimes that support it. This method reduces the internal capacity to match the actual entry count, which can improve enumeration speed and reduce memory usage. However, calling TrimExcess() too often can hurt performance because it forces a reallocation.

Another performance consideration is the allocation of the enumerator itself. In the foreach loop, the compiler uses the GetEnumerator() method. For Dictionary<TKey, TValue>, this returns a struct enumerator, so no heap allocation occurs in the ordinary foreach case. If you manually call GetEnumerator() and keep the enumerator as an interface type like IEnumerator<KeyValuePair<TKey, TValue>>, boxing may occur. In practice, the built-in foreach is efficient and you rarely need to worry about it.

Using LINQ with Dictionary Iteration

LINQ methods like Select, Where, and ToDictionary work naturally with dictionaries because a dictionary implements IEnumerable<KeyValuePair<TKey, TValue>>. This allows you to chain transformations directly on the dictionary.

var upperCaseValues = settings .Where(entry => entry.Key != "port") .Select(entry => entry.Value.ToUpperInvariant());

When you use LINQ, the dictionary is not modified. The methods create deferred queries that are executed when you enumerate the result. If you materialize the result with ToList() or ToArray(), you get a snapshot of the dictionary at that moment. You can then modify the original dictionary while working with the snapshot.

Deferred queries do not bypass the fail-fast rule. If you begin enumerating a query over a dictionary and then add or remove entries before the enumeration finishes, the enumerator will still throw an InvalidOperationException when it detects the version change. Creating a LINQ query does not copy the dictionary, and the exception can occur during the enumeration of the query result.

Edge Cases: Empty Dictionary, Null Values, and Large Collections

An empty dictionary is safe to iterate with foreach; the loop simply does not execute. There is no special handling required. A dictionary can contain null values; during iteration the loop variable is a KeyValuePair containing that null value, so accessing entry.Value is safe.

var data = new Dictionary<int, string?> { [1] = null }; foreach (var entry in data) { Console.WriteLine(entry.Value ?? "null"); }

For very large dictionaries, the foreach loop is generally the fastest way to read all entries. The alternative of using a for loop with ElementAt is much slower because it performs a fresh enumeration up to the requested index each time. If you need indexed access, you should use a List or an array instead of a dictionary.

Another edge case is iterating a dictionary that is being concurrently modified by another thread. A plain Dictionary<TKey, TValue> is not thread-safe. The enumerator may throw the same InvalidOperationException, but you cannot rely on that exception to provide a consistent snapshot. For concurrent access, use ConcurrentDictionary<TKey, TValue>, which provides thread-safe enumeration and modification methods. Its foreach will not throw if the collection is modified, but the iteration order and content may be non-deterministic.

When you understand these behaviors, foreach becomes a reliable tool for reading dictionary contents. The key is to respect the enumeration contract and choose the right approach when you need to modify the dictionary during iteration.

c# dictionary foreach: Practical Usage and Code Examples | RYUSLOG DEV