Back to Blog
C#

C# Dictionary foreach: Iteration Patterns and Pitfalls

c# dictionary foreach: Learn how to iterate over a C# Dictionary using foreach, including key-value pairs, keys, and values, plus common pitfalls like modification dur...

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

c# dictionary foreach requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

When you use foreach with a C# Dictionary, you are iterating over a collection of KeyValuePair<TKey, TValue> structs. This is the most common way to read all entries in a dictionary, but it comes with a few behaviors that can surprise developers who expect list-like iteration. Understanding exactly what the enumerator exposes and how the dictionary behaves during enumeration will help you avoid runtime exceptions and write more predictable code.

The Basic foreach Over a Dictionary

The simplest form of iterating a dictionary is a foreach loop over the dictionary itself. Each iteration yields a KeyValuePair<TKey, TValue> instance, which gives you access to both the 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 is straightforward and works for any dictionary type. The order of iteration is not guaranteed by the .NET runtime. Even though insertion order is often preserved in practice for small dictionaries, you should never rely on it. If you need deterministic ordering, use a SortedDictionary or sort the keys explicitly.

The KeyValuePair struct is a value type, so each iteration copies the pair onto the stack. For most dictionaries this overhead is negligible, but it is worth knowing if you are iterating 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 construct the pair. However, 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 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's Where to create 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> is implemented with an internal array of buckets and entries. The enumerator walks the entry array and skips empty slots. 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. 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. 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. This can be useful when you need to avoid the fail-fast exception while still reading the dictionary.

One subtlety: if the dictionary is modified between the creation of a LINQ query and its execution, the behavior is undefined. The query will still throw InvalidOperationException if the dictionary's version changes. Therefore, LINQ does not give you a free pass to modify the dictionary during enumeration; it only delays the enumeration until you actually iterate the 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. Null values inside a dictionary do not cause any issues during iteration because the KeyValuePair itself is not null. You can access entry.Value even if it is null.

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 linear search 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. The fail-fast exception is not thread-safe; it merely detects that the version changed, but it does not guarantee that you will see 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