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.
Comments
Anonymous
May 07, 2004
The comment has been removedAnonymous
May 08, 2004
Nullable<T> is being changed to only be allowed for ValueTypes later in Whidbey. The reason for this is that the language designers want to avoid seeing API's introduced that take Nullable<string>, and the resulting fracturing that can occur.Anonymous
May 08, 2004
The code we wrote here violates YAGNI. We've written a lot of generality in that we don't know we need. The LazyLoader class varies on 3 axes:
1. Locked or not.
2. Weak or strong reference.
3. delegate construction or new() construction
If you remove one of these features by selecting the option on the left, the class becomes much simpler. If you always select the option on the right, the difference is small.
If you have a need for exactly one combination of these features, you could write that in less code. If you only need it once, you could write it in place, without a new class.
If you need that specific combination in 20 places in your app, and no other combo, then a class that supported exactly that, with no options, would make your code well factored.
If you need the flexibility of all 3 of the above options, then I think the code we've written is about as clear as it possibly can be, with the least duplication. What we've done is write a very full-featured solution, as simply as possible. We're acting like library writers, not application writers. App writers should write the simplest solution, as simply as possible.
It does assume that the reader is comfortable with generics, delegates, an OO concepts. I think that's a perfectly reasonable assumption.Anonymous
May 10, 2004
The comment has been removedAnonymous
May 10, 2004
Sorry if the above comment is hard to read (both codewise and grammarwise). Looks like you can't edit a comment after you've posted. If anyone would like me to clarify anything about that, please let me know.Anonymous
May 19, 2004
The comment has been removedAnonymous
May 19, 2004
JD: I hear ya. I've had some of the same concerns, but not sure where to go with it next.
We'll keep pondering.Anonymous
January 04, 2008
PingBack from http://actors.247blogging.info/?p=4241Anonymous
March 20, 2008
PingBack from http://dinnermoviesblog.info/jaybaz-ms-weblog-the-new-new-lazy-loader/Anonymous
June 16, 2009
PingBack from http://fixmycrediteasily.info/story.php?id=4662