缓存功能结果

Joe*_*orn 7 .net c# caching

为了好玩,我正在玩一个类来轻松缓存功能结果.基本的想法是你可以使用你想要的任何功能 - 虽然你只想将它用于相对昂贵的功能 - 并且可以轻松地将它包装起来使用相对便宜的字典查找,以便以后使用相同的参数运行.真的没什么可说的:

public class AutoCache<TKey, TValue> 
{  
    public AutoCache(Func<TKey, TValue> FunctionToCache)
    {
        _StoredFunction = FunctionToCache;
        _CachedData = new Dictionary<TKey, TValue>();
    }

    public TValue GetResult(TKey Key)
    {
        if (!_CachedData.ContainsKey(Key)) 
            _CachedData.Add(Key, _StoredFunction(Key));
        return _CachedData[Key];
    }

    public void InvalidateKey(TKey Key)
    {
        _CachedData.Remove(Key);
    }

    public void InvalidateAll()
    {
        _CachedData.Clear();
    }

    private Dictionary<TKey, TValue> _CachedData;
    private Func<TKey, TValue> _StoredFunction; 
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,还有一些额外的限制使得它没有那么有用.我们还可以添加一些功能以及实现的其他注意事项.我正在寻找有关如何改善以下任何一点的想法:

  • 这需要一个函数为给定的参数集返回相同的结果(它必须是无状态的).可能没办法改变这个.
  • 它仅限于非常狭窄的代表范围.我们可以扩展它以轻松地工作任何接受至少一个参数并返回值的函数,可能通过在匿名类型中包装参数?或者我们是否需要为我们想要支持的每个Func委托提供额外的实施?如果是这样,我们可以构建一个抽象类来使这更容易吗?
  • 它不是线程安全的.
  • 没有自动失效.这使垃圾收集变得危险.你需要保持它一段时间以使它有用,这意味着你不会真的丢弃旧的和可能不需要的缓存项.
  • 我们可以继承这个,以便在函数只有一个参数的情况下使缓存成为双向的吗?

作为参考,如果我在实际代码中使用它,我认为最可能的地方是作为业务逻辑层的一部分,我使用此代码将数据访问层中的方法包装起来,只从中提取数据查找表.在这种情况下,相对于字典,数据库行程将是昂贵的,并且几乎总是只有一个"键"值用于查找,因此它是一个很好的匹配.

Bar*_*lly 8

这种自动缓存功能结果的另一个名称是memoization.对于公共接口,请考虑以下几点:

public Func<T,TResult> Memoize<T,TResult>(Func<T,TResult> f)
Run Code Online (Sandbox Code Playgroud)

...并简单地使用多态来将T存储在对象的字典中.

扩展代表范围可以通过currying和部分功能应用来实现.像这样的东西:

static Func<T1,Func<T2,TResult>> Curry(Func<T1,T2,TResult> f)
{
    return x => y => f(x, y);
}
// more versions of Curry
Run Code Online (Sandbox Code Playgroud)

由于Curry将多个参数的函数转换为单个参数的函数(但可能返回函数),因此返回值本身有资格进行memoization.

另一种方法是使用反射来检查委托类型,并将元组存储在字典中而不是简单地存储在参数类型中.一个简单的元组只是一个数组包装器,其哈希码和相等逻辑使用深度比较和散列.

弱引用可以帮助失效,但是使用WeakReference键创建字典很棘手 - 最好是在运行时的支持下完成(WeakReference值更容易).我相信那里有一些实现.

通过在内部字典上锁定突变事件可以轻松完成线程安全,但拥有无锁字典可以提高严重并发场景中的性能.那个字典可能更难创建 - 虽然这里一个关于Java的有趣的演示文稿.