An Implementation of Lazy(T) with Invalidation

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>


[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"
}

LazyCache<T> Implementation

And here is my implementation of LazyCache<T>:


public class LazyCache<T>
{
private readonly Func<Lazy<T>> lazyFactory;
private Lazy<T> lazy;
private Lazy<T> Lazy
{
get
{
if (lazy == null)
{
lazy = lazyFactory();
}
return lazy;
}
}

public LazyCache()
{
lazyFactory = () => new Lazy<T>();
}

public LazyCache(bool isThreadSafe)
{
lazyFactory = () => new Lazy<T>(isThreadSafe);
}

public LazyCache(Func<T> valueFactory)
{
lazyFactory = () => new Lazy<T>(valueFactory);
}

public LazyCache(LazyThreadSafetyMode mode)
{
lazyFactory = () => new Lazy<T>(mode);
}

public LazyCache(Func<T> valueFactory, bool isThreadSafe)
{
lazyFactory = () => new Lazy<T>(valueFactory, isThreadSafe);
}

public LazyCache(Func<T> valueFactory, LazyThreadSafetyMode mode)
{
lazyFactory = () => new Lazy<T>(valueFactory, mode);
}

public bool IsValueCreated { get { return Lazy.IsValueCreated; } }
public T Value { get { return Lazy.Value; } }
public void Invalidate()
{
lazy = null;
}
}

Leave a Reply

Your email address will not be published. Required fields are marked *