如何正确使用带有泛型的MemoryCache?

4

我希望使用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)如果缓存中不存在由key标识的缓存条目,它将返回null,并尝试执行(T)null(其中T是值类型),这会引发NullReferenceException异常。

如何为每个T获得类似的行为?

编辑:我已删除where T:class,因为此约束条件会阻止我描述的情况。

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


var item = memoryCache.Get(key) as T; - Ron Beyer
@RonBeyer 我认为 as 只适用于引用类型和可空类型? - Calimero
1
你的 where T : class 使得 T 可空,它不能是值类型,class 只针对引用类型。 - Ron Beyer
1
然后您需要检查类型:var item = memoryCache.Get(key); if (item is T) return (T)item; else return default(T); 或类似的内容。 - Ron Beyer
1
你问:“我该怎么避免这个问题?”但没有说明你想要发生什么。我想你肯定不希望程序崩溃,但是你并没有说出你想要的结果是什么。如果你调用 GetItem<int>("foo") 函数,但 key 值 foo 并不存在,那么会发生什么? - Eric Lippert
显示剩余2条评论
2个回答

6

问题在于如果值为null,则强制转换可能会失败。因此,如果值为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;

现在完全不需要投射了!


2
基于@ericlippart的答案,这看起来是一种好的方法以扩展方法实现,并且有几个问题没有在他的答案中解决:
  1. 您需要一个缓存策略来将loadItemFromDb函数的结果插入到传递给函数的问题中。
  2. Eric的答案中不存在Add方法(具有该签名)。
由于MemoryCache只是抽象类ObjectCache的一个实现,它不使用任何MemoryCache特定的功能,因此我基于ObjectCache进行了此操作。
ObjectCache上泛型版本的AddOrGetExisting方法的完整实现:
    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 存储库 CZEMacLeod/ObjectCacheExtensions 中提供了完整的扩展类,以及 Get 和 TryGetValue 的通用版本,以及其他多个 AddOrGetExisting 的重载版本。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接