对象缓存实现

有这么一种场景,对于某个指定的接口,你需要得到它的一个实例,首先你从一个缓存中取,如果有就返回,否则调用一个指定的 delegate 来得到这个实例。假设有接口和实现:

    public interface IBar { }

    public class Bar : IBar {
        public string Name { get; private set; }
        public Bar(string name) {
            this.Name = name;
        }
    }

    public class TestBar : IBar { 

    }

 

我希望通过某种方式来获取对象, 如果有缓存(假设一个TestBar的对象)就取它,否则 new Bar(“Hello”):

    Cache.Get<IBar>("Hello");

 

我的第一个实现采用了一个Dictionary, 缓存了Type –> Object + Delegate 对:

    public static class DictCache
    {
        private class CachedObj
        {
            public object Instance { get; set; }
            public Delegate Callback { get; set; }
        }

        private static IDictionary<Type, CachedObj> cache = new Dictionary<Type, CachedObj>();

        public static T Get<T>(params object[] arguments)
        {
            CachedObj pair;
            if (!cache.TryGetValue(typeof(T), out pair))
            {
                throw new InvalidOperationException();
            }

            return (T)(pair.Instance ?? pair.Callback.DynamicInvoke(arguments));
        }

        public static void Set<T>(object instance, Expression<Func<T>> exp)
        {
            SetMapping<T>(instance, exp);
        }

        public static void Set<P, T>(object instance, Expression<Func<P, T>> exp)
        {
            SetMapping<T>(instance, exp);
        }

        public static void Set<T>(object instance, LambdaExpression exp)
        {
            SetMapping<T>(instance, exp);
        }

        private static void SetMapping<T>(object instance, LambdaExpression exp)
        {
            cache[typeof(T)] = new CachedObj { Instance = instance, Callback = exp.Compile() };
        }
    }

 

在这个实现里,主要利用了Delegate来延迟创建对象,重载Set 方法是为了方便。

 

首先我们可以缓存这个对象:

            DictCache.Set<string, IBar>(new TestBar(), name => new Bar(name));

 

然后取用:

            IBar bar = DictCache.Get<IBar>("Hello");

 

考虑到在这个实现中使用 Delegate 的 DynamicInvoke 方法,可能会有一些Performance 问题,我的第二个方法使用静态类,并且把Delegate作为类型参数:

    public static class StaticCache
    {
        private static class Cached<T, TDelegate>
        {
            public static T Instance { get; set; }
            public static TDelegate Callback { get; set; }
        }

        public static T Get<T>() where T : class
        {
            return (T) (Cached<T, Func<T>>.Instance ?? Cached<T, Func<T>>.Callback());
        }

        public static T Get<P, T>(P p) where T : class
        {
            return (T)(Cached<T, Func<P, T>>.Instance ?? Cached<T, Func<P, T>>.Callback(p));
        }

        public static void Set<T>(T instance, Expression<Func<T>> exp)
        {
            SetMapping<T, Func<T>>(instance, exp);
        }

        public static void Set<P, T>(T instance, Expression<Func<P, T>> exp)
        {
            SetMapping<T, Func<P, T>>(instance, exp);
        }

        public static void SetMapping<T, TDelegate>(T instance, Expression<TDelegate> exp)
        {
            Cached<T, TDelegate>.Instance = instance;
            Cached<T, TDelegate>.Callback = exp.Compile();
        }
    }

 

这个方案,效率上会有提高,但是需要更多的Get 和 Set 的重载,以针对不同的参数类型和个数, 用法如下:

            StaticCache.Set<string, IBar>(null, name => new Bar(name));

 

获取:

            IBar bar = StaticCache.Get<string, IBar>("name");

 

是否还有其他的更简单的方法呢?发挥我们的创造力!

posted @ 2009-12-03 15:33  Ahha  阅读(928)  评论(1编辑  收藏  举报