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
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!
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With