Calimero
Calimero

Reputation: 299

Proper use of MemoryCache with generics?

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

Answers (2)

Eric Lippert
Eric Lippert

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

Cynthia MacLeod
Cynthia MacLeod

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:

  1. You need a cache policy to insert the result of loadItemFromDb function in the question which isn't passed into the function.
  2. The Add method in Eric's answer doesn't exist (with that signature).

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

Related Questions