The new LazyLoader
Finally, the LazyLoader class. Credit goes to Kevin & Cyrus (who doesn't have a blog).
delegate T Creator<T>();
class LazyLoader<T>
{
IOptional<T> value = new None<T>();
readonly ILock @lock;
readonly Creator<T> create;
public LazyLoader(ILock @lock, Creator<T> create)
{
System.Diagnostics.Debug.Assert(@lock != null);
System.Diagnostics.Debug.Assert(create != null);
if (object.ReferenceEquals(@lock, null))
{
throw new ArgumentNullException("@lock");
}
if (object.ReferenceEquals(create, null))
{
throw new ArgumentNullException("create");
}
this.create = create;
this.@lock = @lock;
}
public T Value()
{
EnsureHaveValue();
return value.Value;
}
void EnsureHaveValue()
{
// PERF: add 'if (value is None<T>)' here
using (this.@lock.Aquire())
{
if (value is None<T>)
{
value = new Some<T>(this.create());
}
}
}
}
To use it, you just need to pass in a locking strategy & and creation delegate.
To make consuming it a little cleaner, we then wrote a factory with 4 variants:
static class LazyCreatorFactory
{
public static Creator<T> Create<T>(ILock @lock, Creator<T> create)
{
return new Creator<T>(new LazyLoader<T>(@lock, create).Value);
}
public static Creator<T> CreateLocked<T>(Creator<T> create)
{
return Create<T>(new Lock(), create);
}
public static Creator<T> CreateUnlocked<T>(Creator<T> create)
{
return Create<T>(new NoLock(), create);
}
public static Creator<T> CreateUnlocked<T>() where T : new()
{
return Create<T>(new NoLock(), delegate { return new T(); });
}
public static Creator<T> CreateLocked<T>() where T : new()
{
return Create<T>(new Lock(), delegate { return new T(); });
}
}
There are two pivot points: lock or not / default ctor or delegate.
Here are some examples of how it looks when you consume it:
class C
{
static Creator<Bitmap> getBitmap = LazyCreatorFactory.CreateLocked<Bitmap>(delegate { return (Bitmap)Bitmap.FromFile(@"c:\jaybaz.bmp"); });
// simple example of using the default ctor
static Creator<object> getObject = LazyCreatorFactory.CreateUnlocked<object>();
public Bitmap Bitmap
{
get
{
return getBitmap();
}
}
static void Main(string[] args)
{
// verify that this really only creates one.
Bitmap m = getBitmap();
Bitmap p = getBitmap();
System.Diagnostics.Debug.Assert(object.ReferenceEquals(m, p));
}
}
Cyrus was pretty excited about this at the end of the session. He said he was going to run back to his office & retrofit his code to use the LazyLoader and the ILock stuff.