I am a big fan of using Lazy<T> as my go-to implementation of the Singleton pattern. It works well and the thread safety is easily adjustable . While I won’t go into the details of how to use it, all the information is in the Lazy<T> documentation.
For what I needed, however, Lazy<T> fell a bit short. I essentially needed a cache of something that was expensive to create, and could be invalidated when the source object changed. In my case, I had a list that I needed to run some aggregate queries against. Running those queries is expensive, so I only want to re-run them when the list has changed, and I want to run them on demand (don’t compute until the last second when it is needed). In other words, I needed a really simple cache.
Thinking it to be easy enough to implement, I created one, using a Lazy<T> as the implementation. My class, the LazyCache<T>, behaves as you would expect Lazy<T> to behave, but with an additional method: Invalidate(). Please note that I didn’t test the thread safety of the LazyCache<T> and already see some potential problems, so if that is a requirement please take steps to ensure that it will behave as needed.
LazyCache<T> Usage
Here is some demo code that uses the LazyCache<T>
[csharp]
[Test] public void LazyCache_DemonstrationWithObservableCollection()
{
var data = new ObservableCollection<string>() { “Hi”, “Howru” };
Func<Dictionary<string, int>> dataAsDic = () => data.ToDictionary(x => x, x => x.Length);
var lazy = new LazyCache<Dictionary<string, int>>(dataAsDic);
data.CollectionChanged += (sender, e) => lazy.Invalidate();
Assert.AreEqual(lazy.Value.Count, 2); // “Hi”, “Howru”
data.Add(“Tree”);
Assert.AreEqual(lazy.Value.Count, 3); // “Hi”, “Howru”, “Tree”
data.RemoveAt(0); // “Howru”, “Tree”
data.RemoveAt(0); // “Tree”
Assert.AreEqual(lazy.Value.Count, 1); // “Tree”
}
[/csharp]
LazyCache<T> Implementation
And here is my implementation of LazyCache<T>:
[csharp]
public class LazyCache
{
private readonly Func
private Lazy
private Lazy
{
get
{
if (lazy == null)
{
lazy = lazyFactory();
}
return lazy;
}
}
public LazyCache()
{
lazyFactory = () => new Lazy
}
public LazyCache(bool isThreadSafe)
{
lazyFactory = () => new Lazy
}
public LazyCache(Func
{
lazyFactory = () => new Lazy
}
public LazyCache(LazyThreadSafetyMode mode)
{
lazyFactory = () => new Lazy
}
public LazyCache(Func
{
lazyFactory = () => new Lazy
}
public LazyCache(Func
{
lazyFactory = () => new Lazy
}
public bool IsValueCreated { get { return Lazy.IsValueCreated; } }
public T Value { get { return Lazy.Value; } }
public void Invalidate()
{
lazy = null;
}
}
[/csharp]