← Back

System.Collections.Frozen — “Create once, read forever” collections in .NET

2026-01-15 15:02 · 👁 41014

#c#

System.Collections.Frozen — “Create once, read forever” collections in .NET

.NET 8 introduced Frozen collections: immutable, highly-optimized lookup collections built for a very specific pattern:

Build infrequently (often once at startup) → read constantly.

The namespace is System.Collections.Frozen, and it currently ships two main types:

  • FrozenDictionary<TKey, TValue>
  • FrozenSet<T>

Both are immutable and optimized for fast lookups, but they also have a higher creation cost than Dictionary / HashSet. That trade-off is the whole point.


Why Frozen exists

A regular Dictionary / HashSet is designed to be mutable: add/remove over time. That flexibility prevents some aggressive optimizations.

A Frozen collection is different: once built, it never changes, so the runtime can use more specialized internal layouts and strategies for lookup speed. Microsoft’s docs describe the intent clearly: high cost to create, excellent lookup performance, ideal for long-lived apps/services.


How to create Frozen collections

You typically create them using extension methods:

  • ToFrozenDictionary()
  • ToFrozenSet()

These build the Frozen structure from an existing sequence/collection.

Example: FrozenDictionary for a hot lookup table
using System.Collections.Frozen;

var httpNames = new Dictionary<int, string>
{
    [200] = "OK",
    [201] = "Created",
    [400] = "Bad Request",
    [401] = "Unauthorized",
    [404] = "Not Found",
    [500] = "Server Error",
}.ToFrozenDictionary();

Console.WriteLine(httpNames[404]); // "Not Found"
Example: FrozenSet for fast membership checks
using System.Collections.Frozen;

var reservedKeywords = new[]
{
    "class", "record", "struct", "interface",
    "public", "private", "protected",
    "async", "await"
}.ToFrozenSet(StringComparer.Ordinal);

bool isKeyword = reservedKeywords.Contains("await"); // true
Case-insensitive keys (common in configs/headers)
using System.Collections.Frozen;

var headers = new[]
{
    KeyValuePair.Create("Content-Type", "application/json"),
    KeyValuePair.Create("User-Agent", "my-app"),
}.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);

Console.WriteLine(headers["content-type"]); // works

When Frozen collections shine

Use Frozen collections when all (or most) of these are true:

Many reads / lookups (TryGetValue, Contains)
✅ Collection is stable (no adds/removes after creation)
✅ Built once (startup, warm-up, cache build)
✅ Used across requests / threads (great for services)

Typical real-world uses:

  • Routing tables, endpoint metadata
  • Token/keyword sets in parsers
  • Feature flag name → handler maps
  • Mapping codes → messages (status, error codes)
  • Validation allow-lists / deny-lists
  • Configuration keys queried repeatedly

Docs explicitly call out this “create once, use often” scenario.


When not to use Frozen

Frozen is not “faster dictionary for everything”.

Avoid it when:

❌ You mutate the collection frequently
❌ The collection is tiny and not performance-critical
❌ You rebuild it often (creation cost may dominate)
❌ You need ordered semantics or frequent enumeration-focused workloads

If you need immutability with frequent updates, ImmutableDictionary / ImmutableHashSet may fit better (persistent data structures), while Frozen is about best possible lookup after construction. (Also: Frozen is only Set/Dictionary today.)


Frozen vs alternatives (quick mental model)

  • Dictionary/HashSet: best general-purpose, mutable
  • ReadOnlyDictionary: read-only wrapper, not a new optimized data structure
  • ImmutableDictionary/ImmutableHashSet: immutable + efficient “modified copies”
  • FrozenDictionary/FrozenSet: immutable + max lookup performance, but expensive build

A practical pattern: build once at startup

using System.Collections.Frozen;

public static class LookupTables
{
    public static readonly FrozenDictionary<string, int> CountryCodes =
        new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
        {
            ["BR"] = 55,
            ["US"] = 1,
            ["PT"] = 351
        }.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);
}

Now you get:

  • Fast lookups
  • Thread-safe sharing (immutable)
  • Zero accidental mutation

Pro tip: measure before/after

Frozen collections can be a win in hot paths, but don’t guess—benchmark with BenchmarkDotNet in your scenario (key type, size, access pattern). This is especially true because some key types already hash perfectly (e.g., ints), so gains may be smaller than with strings or complex comparers.


Wrap-up

If you have a lookup table that is:

built once + read constantly, Frozen collections are one of the cleanest “drop-in” performance upgrades in modern .NET.


#dotnet #csharp #performance #collections #backend #systemcollectionsfrozen

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please reload the page.