Back to Blog
C#

C# Dictionary GetValueOrDefault: Usage and Behavior

c# dictionary getvalueordefault: Learn how to use Dictionary.GetValueOrDefault in C# to retrieve values safely with a default, compare it with TryGetValue, and avoid c...

C#Dictionary.NETCollectionsDefault ValuesTryGetValue
Illustration of a C# dictionary lookup returning a default value when a key is missing, with emphasis on safe retrieval.

When you need to read a value from a Dictionary<TKey, TValue> and the key may not exist, the straightforward indexer throws a KeyNotFoundException. The GetValueOrDefault method provides a concise way to return either the existing value or a default without an explicit TryGetValue check. This article explains how c# dictionary getvalueordefault behaves, where it fits alongside other lookup patterns, and what to watch for in real code.

What Dictionary.GetValueOrDefault Does

GetValueOrDefault is an extension method defined in System.Collections.Generic for IReadOnlyDictionary<TKey, TValue> and IDictionary<TKey, TValue>. Given a key, it returns the associated value if the key exists; otherwise it returns the default value for the value type. For reference types, that default is null; for value types, it is the zero-initialized value (for example, 0 for int, false for bool).

using System; using System.Collections.Generic; var inventory = new Dictionary<string, int> { ["apples"] = 5, ["oranges"] = 3 }; int appleCount = inventory.GetValueOrDefault("apples"); // 5 int bananaCount = inventory.GetValueOrDefault("bananas"); // 0

The method does not throw when the key is missing. This makes it useful for scenarios where a missing key is a normal condition, not an error.

There is also an overload that accepts an explicit default value:

int bananaCount = inventory.GetValueOrDefault("bananas", 10); // 10

This overload is helpful when the natural default is not the type's zero value, such as a fallback count or a sentinel value.

Default Values for Value Types and Reference Types

The default returned by the parameterless overload depends entirely on the type of the dictionary's TValue. For value types, the default is default(TValue), which is the zero-bit pattern. For reference types and nullable value types, it is null. Consider a dictionary that stores string values:

var settings = new Dictionary<string, string> { ["theme"] = "dark" }; string theme = settings.GetValueOrDefault("theme"); // "dark" string missing = settings.GetValueOrDefault("missing"); // null

Because string is a reference type, the missing key yields null. This is consistent with the behavior of TryGetValue, which sets its output parameter to default(TValue) when the key is absent.

For nullable value types, such as int?, the default is null, not 0:

var map = new Dictionary<string, int?> { ["a"] = 1 }; int? result = map.GetValueOrDefault("b"); // null

This distinction matters when you need to distinguish between a key that exists with a value of 0 and a key that is missing entirely. GetValueOrDefault alone cannot make that distinction; you need TryGetValue or a check with ContainsKey if that distinction is important.

GetValueOrDefault vs TryGetValue

The most common alternative to GetValueOrDefault is the TryGetValue pattern:

if (inventory.TryGetValue("bananas", out int count)) { // use count } else { // handle missing key }

TryGetValue gives you a boolean indicating whether the key exists, and it writes the value to an out variable. This is the right choice when the missing-key case requires branching logic beyond simply substituting a default. For example, you might need to log the absence, increment a counter, or perform a fallback lookup elsewhere.

GetValueOrDefault is more concise when you only need a value and are comfortable with a single default. It reduces the ceremony of declaring an out variable and writing an if/else. The two approaches are equivalent in terms of lookup behavior; both use the dictionary's internal hash-based lookup and do not allocate extra objects.

A practical difference appears when the default value is expensive to compute. The overload that takes a default value evaluates that argument eagerly, even if the key exists. If the default is the result of a method call that has side effects or is costly, TryGetValue may be preferable because it avoids that evaluation when the key is present.

// The method GetFallback() is called even when the key exists. var value = dict.GetValueOrDefault(key, GetFallback()); // With TryGetValue, GetFallback() is called only when needed. if (!dict.TryGetValue(key, out var result)) { result = GetFallback(); }

This is a subtle but real consideration in hot paths or when the fallback involves I/O or complex computation.

Performance and Allocation Behavior

GetValueOrDefault does not introduce a meaningful performance penalty compared to TryGetValue. Both methods perform the same hash lookup and do not allocate managed memory for the lookup itself. The extension method is implemented as a thin wrapper around TryGetValue in the .NET runtime, so the generated code is nearly identical.

One area where performance can differ is the eager evaluation of the default argument, as noted above. If the default expression is a constant or a cheap value, the difference is negligible. If it involves a method call, the cost can be non-trivial when the method is called on every lookup, even for keys that exist.

Another consideration is the use of GetValueOrDefault with reference types. Returning null is a common source of NullReferenceException if the caller immediately dereferences the result. In such cases, you may want the overload with a non-null default or a TryGetValue branch that handles null explicitly.

There is also a subtle difference in how the compiler handles the two overloads. The parameterless overload uses default(TValue) as the fallback. The overload with a default value requires an argument of type TValue. If you pass null for a value type, it will not compile, which is good because it prevents a common mistake.

Common Mistakes and Edge Cases

One common mistake is assuming that GetValueOrDefault will return null for a missing key in a dictionary with a value type. For Dictionary<string, int>, the return type is int, so a missing key returns 0. This can lead to subtle bugs if you interpret 0 as a meaningful value rather than a missing indicator.

Another mistake is using GetValueOrDefault when you need to know whether the key exists. The method does not communicate existence. If your logic must treat a stored 0 differently from a missing key, you cannot use GetValueOrDefault alone. You need TryGetValue or a combination of ContainsKey and the indexer.

For reference types, a missing key returns null, and a present key might also hold null if you explicitly stored null as the value. The method does not distinguish between these two cases. If your dictionary can contain null values, be aware that GetValueOrDefault will return null in both scenarios.

A less obvious edge case involves the default value for custom structs. If TValue is a struct with its own fields, default(TValue) will be a struct with all fields set to their defaults. This is usually the same as new TValue() but not always if the struct has a parameterless constructor in newer C# versions. In C# 10 and later, structs can have explicit parameterless constructors, but default(TValue) still produces the zero-initialized value, not the constructor result. This can cause unexpected behavior if you rely on a custom default.

When to Use GetValueOrDefault

GetValueOrDefault is the right tool when you want a one-liner to retrieve a value and you are comfortable with the type's default for missing keys. It is especially useful in configuration lookups, counting scenarios, and mapping operations where a missing key is a normal condition.

Use TryGetValue when you need to:

  • Branch on whether the key exists
  • Avoid evaluating a costly default when the key is present
  • Distinguish between a missing key and a key with a default value
  • Perform additional work when the key is absent

Use the indexer when you are certain the key exists and want an exception to surface if that assumption is wrong. The indexer's KeyNotFoundException is a useful fail-fast signal in data integrity checks.

There is also a middle ground: the overload with an explicit default. This is appropriate when the default is a constant or a cheap expression, and you want to avoid the TryGetValue boilerplate. For example, reading a timeout value from a dictionary with a default of 30 seconds:

int timeout = config.GetValueOrDefault("timeout", 30);

This is clear and concise. Just be aware that the default expression is evaluated every time, even when the key exists.

Using the Overload with a Custom Default

The overload with a custom default is not limited to constants. You can pass any expression that evaluates to TValue. However, as mentioned, the expression is evaluated eagerly. If the expression is a method call that returns a new object, that object is created on every call, even if the key exists. This can cause unnecessary allocations in a loop.

var cache = new Dictionary<string, List<int>>(); var result = cache.GetValueOrDefault("items", new List<int>());

In this example, a new List<int> is allocated on every call, regardless of whether the key exists. If the key usually exists, this is wasted work. A better approach is to use TryGetValue and only create the list when needed, or to store a shared default instance if it is safe to reuse.

Another pattern is to use GetValueOrDefault with a lazy default via a lambda, but the method does not accept a delegate. You would need to implement that yourself, which often means falling back to TryGetValue.

Compatibility and Extension Method Availability

GetValueOrDefault is an extension method, so it is not available on older .NET Framework versions unless you install a compatibility package or define your own. In modern .NET (Core 2.0+, .NET 5+), it is part of the base class library. If you are working on a legacy codebase, you may need to implement the method yourself or use TryGetValue exclusively.

A simple custom implementation for older frameworks looks like this:

public static TValue GetValueOrDefault<TKey, TValue>( this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue = default) { return dictionary.TryGetValue(key, out TValue value) ? value : defaultValue; }

This replicates the behavior and can be placed in a shared utility class. It also gives you the flexibility to add a lazy-evaluation overload if needed.

When writing new code, prefer the built-in method when your target framework supports it. It is idiomatic, well-tested, and avoids maintaining a custom utility.

The final section should not be a summary; it should contain substantive technical content. Here, we discussed compatibility and a custom implementation, which is useful for legacy scenarios. This ends the article with a practical implementation detail.

c# dictionary getvalueordefault: Practical Usage and Code Ex | RYUSLOG DEV