对象缓存实现
有这么一种场景,对于某个指定的接口,你需要得到它的一个实例,首先你从一个缓存中取,如果有就返回,否则调用一个指定的 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");
是否还有其他的更简单的方法呢?发挥我们的创造力!