Skip to content

CacheExtensions.TryGetValue<T> documentation and behavior are ambiguous for type mismatches (key present with incompatible type returns false) #11958

@rjmurillo

Description

@rjmurillo

Description

When using Microsoft.Extensions.Caching.Memory.IMemoryCache with the generic extension method:

IMemoryCache cache = ...;
cache.Set("k", (object)"string-value");

// Under the hood: IMemoryCache.TryGetValue("k", out object? obj) == true
// But the generic extension below returns false because obj is not int.
bool found = cache.TryGetValue<int>("k", out var i); // found == false

This reveals two problems:

  1. Behavioral ambiguity for instrumentation / decorators
    A decorator that instruments IMemoryCache.TryGetValue(object, out object?) will observe a hit (returns true because the key exists), while the calling code using CacheExtensions.TryGetValue<T> observes a miss (false due to type mismatch). This makes consistent hit/miss accounting impossible when consumers use the generic extension.

  2. Documentation mismatch
    The current docs for CacheExtensions.TryGetValue<T> say: “true if the key was found; false otherwise.” In the scenario above, the key is found but the method returns false. The real behavior is closer to:

“Returns true if the key was found and the stored value is of type TItem (otherwise returns false and sets return value to default).”

Reproduction Steps

using Microsoft.Extensions.Caching.Memory;

var cache = new MemoryCache(new MemoryCacheOptions());

// Store as object/string
cache.Set("key", (object)"abc");

// 1) Non-generic call sees "found"
var foundObj = cache.TryGetValue("key", out object? obj); // true

// 2) Generic extension reports "not found" for incompatible type
var foundInt = cache.TryGetValue<int>("key", out var i);   // false

// Instrumented decorators counting "hits" in IMemoryCache.TryGetValue(object, out object?)
// will count the first as a hit, while callers using the generic extension see a miss.
Console.WriteLine((foundObj, foundInt)); // (True, False)

Expected behavior

Docs and API guidance should clarify that the generic overload only returns true when the value exists and is compatible with TItem.

Expected behavior

  • CacheExtensions.TryGetValue<T> documentation should explicitly state:

"Returns true if the key was found and the stored value can be cast to TItem. Returns false otherwise (including type mismatches)."

  • Guidance should explain implications for decorators/instrumented caches.
  • (Optional stretch) Consider an additional API if presence-vs-type disambiguation is needed, but a doc fix alone would resolve most confusion.

Actual behavior

  • Non-generic IMemoryCache.TryGetValue(object, out object?) returns true if the key is present.
  • Generic CacheExtensions.TryGetValue<T> returns false if the type does not match, even when the key exists.
  • Documentation implies only the first behavior ("true if the key was found”'), without mention of the type check.

Regression?

No. This appears to be the behavior since the generic extension was introduced. The issue is a documentation mismatch and missing guidance rather than a runtime regression

Known Workarounds

  • Use the non-generic overload when consistent hit/miss metrics are required in decorators.
  • Or, after a generic call returns false, double-check with the non-generic overload if you need to distinguish "key present with wrong type" from "key absent" scenario.

Configuration

No response

Other information

No response

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions