If your application builds a lookup table once and reads it throughout its lifetime, consider FrozenDictionary<TKey, TValue>. It does extra work during construction to optimize later lookups. That tradeoff fits fixed mappings and stable reference data.

.NET 8 introduced the System.Collections.Frozen namespace.

Build once, reuse across calls

Keep the frozen dictionary outside the method that performs the lookup:

using System.Collections.Frozen;

public static class ErrorStatusCodes
{
    private static readonly FrozenDictionary<string, int> ByCode =
        new Dictionary<string, int>
        {
            ["validation_failed"] = 400,
            ["not_found"] = 404,
            ["conflict"] = 409
        }.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);

    public static int Resolve(string code) =>
        ByCode.TryGetValue(code, out var status) ? status : 500;
}

The static field keeps one instance for repeated calls. If you move ToFrozenDictionary() into Resolve(), every lookup rebuilds the table.

Pass the comparer explicitly when converting. Here, "NOT_FOUND" and "not_found" intentionally resolve to the same entry. Without a comparer argument, ToFrozenDictionary() uses the default key comparer, even if the source dictionary uses a different one.

The three entries illustrate the lifetime and API, not a measured speedup. A small Dictionary or switch may already be sufficient.

Know what is frozen

A static readonly Dictionary still lets you change entries. readonly only prevents reassigning the field. A FrozenDictionary cannot be modified after construction.

It does not freeze objects stored as values. A FrozenDictionary<string, List<string>> still contains mutable lists. Use immutable values when callers need a stable snapshot.

Build from trusted keys. The API documentation warns that key details affect construction time.

When to use it

Use it for stable tables with enough repeated reads to justify construction. Benchmark your real key set and comparer, including successful and missing lookups. Measure construction separately. There is no universal size threshold or guaranteed speedup.

Keep a regular Dictionary when entries change frequently or the table is only used briefly. A ConcurrentDictionary is an option when the same collection needs concurrent reads and writes. If the data needs expiration, refresh, or invalidation, design that separately. A frozen table does not provide cache behavior.