有這麼一種情境,對於某個指定的介面,你需要得到它的一個執行個體,首先你從一個緩衝中取,如果有就返回,否則調用一個指定的 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");
是否還有其他的更簡單的方法呢?發揮我們的創造力!