C#泛型缓存,类型安全

时间:2009-08-07 13:39:29

标签: c# generics caching

我正在寻找一种为任何对象提供通用本地缓存的方法。这是代码:

    private static readonly Dictionary<Type,Dictionary<string,object>> _cache 
        = new Dictionary<Type, Dictionary<string, object>>();

    //The generic parameter allow null values to be cached
    private static void AddToCache<T>(string key, T value)
    {
        if(!_cache.ContainsKey(typeof(T)))
            _cache.Add(typeof(T),new Dictionary<string, object>());

        _cache[typeof (T)][key] = value;
    }

    private static T GetFromCache<T>(string key)
    {
        return (T)_cache[typeof (T)][key];
    }   

1-有没有办法不使用getfromcache方法?

2-有没有办法确保第二个字典中的类型安全,假设所有对象都具有相同的类型。 (这是由addToCache方法提供的,但我更喜欢设计中的类型控件)。例如,要具有以下类型的_cache

    Dictionary<Type,Dictionary<string,typeof(type)>>

THX

7 个答案:

答案 0 :(得分:12)

试试这个:

static class Helper<T>
{
       internal static readonly Dictionary<string, T> cache = new Dictionary<string, T>();
}
private static void AddToCache<T>(string key, T value)
{
   Helper<T>.cache[key] = value;
}
private static T GetFromCache<T>(string key)
{
    return Helper<T>.cache[key];
}

答案 1 :(得分:3)

为什么不将泛型参数放到类声明中:

public class Cache<T>
{
    private Dictionary<string, T> _cache = new Dictionary<string, T>();
    ...

}

如果您愿意,可以是静态的

答案 2 :(得分:1)

除非原始类型没有装箱,否则你不会获得太多收益。

private static readonly Dictionary<Type,Dictionary<string,object>> _cache 
    = new Dictionary<Type, IDictionary>();

//The generic parameter allow null values to be cached
private static void AddToCache<T>(string key, T value)
{
    // create a dictionary of the correct type
    if(!_cache.ContainsKey(typeof(T)))
        _cache.Add(typeof(T),new Dictionary<string, T>());

    _cache[typeof (T)][key] = value;
}

private static T GetFromCache<T>(string key)
{
    // casting the dictionary instead of the value
    Dictionary<string, T> typedDictionary = (Dictionary<string, T>)_cache[typeof (T)];
    return typedDictionary[key];
}

当然需要更多未找到处理。

答案 3 :(得分:1)

IMemoryCache

自举:

services.AddMemoryCache();

用法:

public class HomeController : Controller
{
    private IMemoryCache _cache;

    public HomeController(IMemoryCache memoryCache)
    {
        _cache = memoryCache;

        var onlineAt = _cache.Get<DateTime?>("self:startup");
    }

有关写入和引导详细信息,请查看上面的链接。它结合了我的答案所包含的大部分内容。您还可以使用Microsoft.Extensions.Caching.Redis或其他分布式缓存。

这是现在的'官方'方法。


后代的原始答案

我必须把它扔出去,这看起来不像你应该做的事情。通过使用依赖注入/控制反转库,Enterprise Library CacheManager之类的工具或使用分布式内存缓存程序(如Memcache或Microsoft Velocity),无论您做什么都可能更好。

答案 4 :(得分:0)

如果在编译时不知道所需的类型,则无法创建强类型的缓存。如果你在运行时之前不知道所需的类型,那么问题的答案是否定的,我很害怕。

答案 5 :(得分:0)

应该放入几个锁,也许还有几个克隆......但这应该可以。

class Program
{
    static void Main(string[] args)
    {
        CacheObject<int>.Instance["hello"] = 5;
        CacheObject<int>.Instance["hello2"] = 6;
        CacheObject<string>.Instance["hello2"] = "hi";
        Console.WriteLine(CacheObject<string>.Instance["hello2"]); //returns hi
    }
}

public class CacheObject<V> : CacheObject<string, V> { }
public class CacheObject<K,V>
{
    private static CacheObject<K, V> _instance = new CacheObject<K, V>();
    public static CacheObject<K, V> Instance { get { return _instance; } }

    private Dictionary<K, V> _store = new Dictionary<K, V>();
    public T this[K index]
    {
        get { return _store.ContainsKey(index) ? _store[index] : default(V); }
        set
        {
            if (_store.ContainsKey(index)) _store.Remove(index);
            if (value != null) _store.Add(index, value);
        }
    }
}

答案 6 :(得分:0)

即使这是一个较旧的线程,它也会在我今天运行的搜索中弹出...这是@Daniel帖子的更新版本,它实现了线程安全(如多个良好注释中所述)以及自我-填充/惰性缓存初始化:

public static class SimpleLazyCacheHelper<T>
{
    private static readonly ConcurrentDictionary<string, Lazy<T>> cache = new ConcurrentDictionary<string, Lazy<T>>();

    [Obsolete("This method adds existing values to the cache, but it's best to use GetOrAddToCache() to facilitate a self-populating cache (especially in a Web environment)")]
    public static void AddToCache(string key, T value)
    {
        cache.TryAdd(key, new Lazy<T>(() => value));
    }

    [Obsolete("This method returns only existing values; it's best to initialize cache values via GetOrAddToCache()")]
    public static T GetFromCache(string key)
    {
        return cache[key].Value;
    }

    /// <summary>
    /// Provides a self-populating/blocking approach to the cache so that ALL Threads wait for the first thread that requested a cache key
    ///     to be initialized and populated into the cache. This means that they will always wait less time than if they did all the
    ///     work itself.  Very useful for long running work (e.g. DB calls, I/O processing, etc).
    /// More info. on the value fo self-populating approach is here:
    ///     https://www.ehcache.org/documentation/2.8/apis/constructs.html
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <param name="valueFactory"></param>
    public static T GetOrAddToCache(string key, Func<T> valueFactory)
    {
        return cache.GetOrAdd(key, new Lazy<T>(valueFactory)).Value;
    }
}

用法是:

CacheHelper<MyCacheableClass>.GetOrAddToCache($"UniqueDynamicKey", () =>
{
     //
     //... do some long running work...
     //
     return new MyCacheableClass();
});

这里也是要点:https://gist.github.com/cajuncoding/e2ff490d79812c83bedfd5a77888e727

对于任何正在寻找我们在生产中使用的充实解决方案的人,您可以在我的LazyCachHelpers项目中看到相同的模式: https://github.com/cajuncoding/LazyCacheHelpers