The new new lazy loader
Cyrus then incorporated the Weak/Strong reference stuff into the LazyLoader. He also refactored the factory to give you a reliable, predictable default & be a bit simpler. (You also need Optional<> and the Lock<> code.)
First, the delegate definition:
delegate A Creator<A>();
Then the LazyLoader class:
class LazyLoader<A>
{
readonly IReference<A> reference;
readonly ILock @lock;
readonly Creator<A> create;
public LazyLoader(IReference<A> reference, ILock @lock, Creator<A> create)
{
Debug.Assert(@lock != null);
Debug.Assert(create != null);
Debug.Assert(reference != null);
if (object.ReferenceEquals(@lock, null))
{
throw new ArgumentNullException("@lock");
}
if (object.ReferenceEquals(create, null))
{
throw new ArgumentNullException("create");
}
if (object.ReferenceEquals(reference, null))
{
throw new ArgumentNullException("reference");
}
this.reference = reference;
this.create = create;
this.@lock = @lock;
}
public A Value
{
get
{
using (this.@lock.Aquire())
{
IOptional<A> optional = reference.Target;
if (optional is Some<A>)
{
return optional.Value;
}
A value = this.create();
reference.Value = value;
return value;
}
}
}
//only exists since we can't pass the above property to a delegate's constructor
public A GetValue()
{
return this.Value;
}
}
Note that Value is again a property, and GetValue is the method that we pass to a delegate.
Note also that there is no inheritance; it all fell out of the system.
And finally the factory class:
static class LazyCreatorFactory
{
public static Creator<A> Create<A>(IReference<A> reference, ILock @lock, Creator<A> create)
{
return new Creator<A>(new LazyLoader<A>(reference, @lock, create).GetValue);
}
public static Creator<A> Create<A>(bool strong, bool locked, Creator<A> create)
{
IReference<A> reference;
if (strong)
{
reference = new StrongReference<A>();
}
else
{
reference = new WeakReference<A>();
}
ILock @lock;
if (locked)
{
@lock = new Lock();
}
else {
@lock = new NoLock();
}
return Create<A>(reference, @lock, create);
}
public static Creator<T> Create<T>(bool strong, bool locked) where T : new()
{
return Create<T>(strong, locked, delegate { return new T(); });
}
/// a simple constructor with smart safe defaults
public static Creator<T> Create<T>(Creator<T> create)
{
return Create<T>(true, true, create);
}
}
Here’s what it looks like to consume it:
// some examples of how this looks for the consumer.
class C
{
static Creator<Bitmap> getBitmap = LazyCreatorFactory.Create<Bitmap>(delegate { return (Bitmap)Bitmap.FromFile(@"c:\menu6.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));
}
}
Note that the instance is now named like a verb (getBitmap) and there’s a trivial property that consumes it, giving you the same appearance as a regular property-with-backing-store.