Reputation: 299
I wish to use System.Runtime.Caching.MemoryCache
but I'm wondering how to use it with generics.
In the following example, I would be in trouble if T
is a value type.
public T GetItem<T>(string key, Func<T> loadItemFromDb)
{
var cachedItem = (T) memoryCache.Get(key);
if(cachedItem != null)
return cachedItem;
// call db
//put result in cache
// return result
}
MemoryCache.Get(string key)
returns null
if the cache entry identified by key
doesn't exist and it would raise NullReferenceException
as it tries to do (T)null
(with T
a value type)
How could I get similar behaviour for every T
?
EDIT : I removed where T : class
as this constraint prevents the case I'm describing.
EDIT 2 : I add some code to provide intent
Upvotes: 4
Views: 5980
Reputation: 660417
The problem is that the cast can fail if the value is null. So don't cast if the value is null.
public T GetItem<T>(string key, Func<T> loadItemFromDb)
{
object cachedItem = memoryCache.Get(key);
if (cachedItem is T)
return (T)cachedItem;
T item = loadItemFromDb();
memoryCache.Add(key, item, somePolicy);
return item;
}
There's no problem with value types here; if T is a value type, and cachedItem
is not a boxed T
, then we never cast cachedItem
to T
.
FYI in C# 7 you can tighten that up a bit to:
if (cachedItem is T t)
return t;
and now there's no cast at all!
Upvotes: 6
Reputation: 61
Based on @ericlippart's answer, this looked like a good method to implement as an extension method and there are a couple of issues not addressed in his answer:
As MemoryCache
is just an implementation of the abstract class ObjectCache
and it does not use any MemoryCache
specific functionallity, I based this on ObjectCache
.
A full implementation of the Generic version of the AddOrGetExisting
method on ObjectCache
:
public static T AddOrGetExisting<T>(ObjectCache cache, string key, Func<(T item, CacheItemPolicy policy)> addFunc)
{
object cachedItem = cache.Get(key);
if (cachedItem is T t)
return t;
(T item, CacheItemPolicy policy) = addFunc();
cache.Add(key, item, policy);
return item;
}
Note that this uses the C# 7 enhancement mentioned and also System.ValueTuple
which is available from NuGet if using net461 or higher or is built into netstandard2.0
A full extension class, along with generic versions of Get and TryGetValue, as well as a number of other overloads of AddOrGetExisting is available in my GitHub repository CZEMacLeod/ObjectCacheExtensions
Upvotes: 3