Back to Blog
C#

Removing Entries from a C# Dictionary

c# dictionary remove: Learn C# Dictionary remove syntax, overloads, missing-key behavior, enumeration pitfalls, and concurrent removal with ConcurrentDictionary.

C#Dictionary.NETConcurrentDictionaryCollections
Editorial illustration of a C# dictionary key-value card being removed from a stack, representing the Dictionary Remove method.

The C# dictionary Remove method is a single call, but its return value, overloads, and runtime behavior affect how you should use it in production code. The basic form is Remove(key), which returns a bool telling you whether the key actually existed before the removal.

The Remove Method and Its Overloads

The Dictionary<TKey, TValue> class exposes two Remove overloads. The first takes only the key:

var config = new Dictionary<string, string> { ["env"] = "production", ["region"] = "us-east-1" }; bool removed = config.Remove("env");

Remove(key) returns true when the key was present and the entry was removed, and false when the key was not found. A failed removal does not throw and does not modify the dictionary. This makes the return value a reliable signal for logic that must know whether a deletion actually happened.

The second overload, available in .NET Core 2.0 and later, also returns the removed value:

if (config.Remove("region", out string region)) { Console.WriteLine($"Removed region {region}"); }

The out parameter receives the value that was stored under the key. When the key is absent, the method returns false and region is set to default(string), which is null. This overload is useful when you need the value at the moment of removal, because it avoids a separate TryGetValue call followed by a second Remove.

What Happens When the Key Is Missing

The return value is the primary signal for whether a removal occurred. Remove does not throw when the key does not exist, so you do not need a ContainsKey check before calling it. In fact, calling ContainsKey first and then Remove is a redundant lookup that doubles the hash work.

The one exception is a null key. Remove(null) throws ArgumentNullException because Dictionary<TKey, TValue> does not allow null keys. If your key type is a reference type and the key can be null, guard the call:

if (key is not null) { dictionary.Remove(key); }

For value types like int or Guid, a null key is impossible, so the guard is unnecessary.

Removing Entries While Enumerating

A common mistake is removing entries inside a foreach loop over the dictionary itself:

foreach (var kvp in config) { if (kvp.Value == "stale") { config.Remove(kvp.Key); // throws InvalidOperationException } }

The Dictionary enumerator detects modification and throws InvalidOperationException on the next MoveNext call. The fix is to collect the keys you want to remove first, then remove them after the loop:

var staleKeys = config .Where(kvp => kvp.Value == "stale") .Select(kvp => kvp.Key) .ToList(); foreach (var key in staleKeys) { config.Remove(key); }

The ToList() call materializes the key collection before any removal happens, so the dictionary is not modified while it is being enumerated. This pattern works for any removal predicate, not just value comparisons.

Remove Versus Clear

Clear() removes every entry from the dictionary. It is the right choice when the dictionary will be discarded or rebuilt from scratch. Remove is for deleting a specific entry while keeping the rest of the dictionary intact.

One behavior worth knowing: Clear() does not release the internal capacity. The dictionary keeps its underlying buckets and entry array allocated, so a subsequent Add does not need to resize immediately. If you want to release that memory, replace the dictionary reference with a new instance or call TrimExcess on the cleared dictionary to shrink the internal arrays to the current count.

Performance Cost of Removal

Removal from a Dictionary is O(1) on average because the entry is located through a hash lookup rather than a scan. The worst case is O(n) when many keys collide in the same bucket, which is why a poorly distributed hash function or a badly chosen initial capacity can degrade removal along with every other dictionary operation.

Removal does not shrink the dictionary's capacity. The freed slot becomes available for future inserts, but the internal arrays stay allocated at their current size. If you remove a large number of entries and keep the dictionary alive for a long time, the memory for the empty capacity remains reserved. In that situation, calling TrimExcess after the removals, or replacing the dictionary with a freshly constructed one, releases the unused capacity.

Thread Safety and Concurrent Removal

A plain Dictionary<TKey, TValue> is not safe for concurrent reads and writes. If one thread calls Remove while another thread reads or writes the same dictionary, the behavior is undefined and can corrupt internal state. For concurrent access, use ConcurrentDictionary<TKey, TValue>:

var cache = new ConcurrentDictionary<string, byte[]>(); if (cache.TryRemove("session-42", out byte[] payload)) { // payload was removed atomically }

TryRemove performs the removal as a single atomic operation and returns true only if the key existed. This is the correct API when multiple threads can remove the same key, because it avoids the race between a ContainsKey check and a separate Remove call. The plain Dictionary has no equivalent atomic remove-and-report operation, which is another reason to switch to ConcurrentDictionary when the dictionary is shared.

Removing by Predicate Without a Built-In Method

Unlike List<T>, which has RemoveAll, Dictionary<TKey, TValue> has no method that removes every entry matching a predicate. The collect-then-remove pattern from the enumeration section is the standard way to implement it, but it is worth wrapping in a small helper when the predicate is used in more than one place:

public static int RemoveWhere<TKey, TValue>( this Dictionary<TKey, TValue> dictionary, Func<KeyValuePair<TKey, TValue>, bool> predicate) { var keys = dictionary .Where(predicate) .Select(kvp => kvp.Key) .ToList(); foreach (var key in keys) { dictionary.Remove(key); } return keys.Count; }

The helper returns the number of removed entries, which is useful for logging or for deciding whether a follow-up action is needed. The materialized key list is the critical part: without it, the removal would modify the dictionary during enumeration and throw InvalidOperationException.

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