-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
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 == falseThis reveals two problems:
-
Behavioral ambiguity for instrumentation / decorators
A decorator that instrumentsIMemoryCache.TryGetValue(object, out object?)will observe a hit (returnstruebecause the key exists), while the calling code usingCacheExtensions.TryGetValue<T>observes a miss (falsedue to type mismatch). This makes consistent hit/miss accounting impossible when consumers use the generic extension. -
Documentation mismatch
The current docs forCacheExtensions.TryGetValue<T>say: “true if the key was found; false otherwise.” In the scenario above, the key is found but the method returnsfalse. The real behavior is closer to:
“Returns
trueif the key was found and the stored value is of typeTItem(otherwise returnsfalseand sets return value todefault).”
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?)returnstrueif the key is present. - Generic
CacheExtensions.TryGetValue<T>returnsfalseif 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