正确使用 MemoryCache 和泛型?

Proper use of MemoryCache with generics?

我想使用 System.Runtime.Caching.MemoryCache 但我想知道如何将它与泛型一起使用。

在下面的示例中,如果 T 是值类型,我会遇到麻烦。

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 如果由 key 标识的缓存条目不存在并且它会在尝试执行时引发 NullReferenceException (T)null(具有 T 值类型)

我怎样才能为每个 T 获得类似的行为?

编辑:我删除了 where T : class,因为此约束阻止了我正在描述的情况。

编辑 2:我添加了一些代码来提供意图

问题是如果值为空,转换可能会失败。所以如果值为 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;
}

这里的值类型没有问题;如果 T 是值类型,并且 cachedItem 不是装箱的 T,那么我们永远不会将 cachedItem 转换为 T.

仅供参考,在 C# 7 中,您可以将其收紧一点:

    if (cachedItem is T t)
       return t;

现在根本没有演员!

根据@ericlippart 的回答,这看起来是作为扩展方法实现的好方法,他的回答中有几个问题没有解决:

  1. 您需要一个缓存策略来将 loadItemFromDb 函数的结果插入到未传递给函数的问题中。
  2. Eric 的回答中的 Add 方法不存在(带有那个签名)。

由于 MemoryCache 只是抽象 class ObjectCache 的实现,它不使用任何 MemoryCache 特定功能,我基于 ObjectCache.

AddOrGetExisting 方法的通用版本在 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;
    }

请注意,这使用了提到的 C# 7 增强功能以​​及 System.ValueTuple,如果使用 net461 或更高版本,则可从 NuGet 获得,或者内置于 netstandard2.0

我的 GitHub 存储库 GitHub[=] 中提供了完整的扩展 class,以及 Get 和 TryGetValue 的通用版本,以及 AddOrGetExisting 的许多其他重载19=]