Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
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.
Comments
- Anonymous
May 06, 2004
So what is this "@" before the "lock"? ("@lock")?
New kind of syntax? - Anonymous
May 07, 2004
public static Creator<T> Create<T>(ILock @lock, Creator<T> create)
{
return new Creator<T>(new LazyLoader<T>(@lock, create).Value);
}
At first I was confused by that code (generics+delegate make it a bit hard to read, imo), because it looks like Value is actually called there (but it's in fact a method, not a property...).
Maybe you could add some comment there: // Makes a lazy/thread-safe Creator out of Creator
Is there a way to remove the "new Creator<T>(" part?
Maybe the implicit delegate syntax could help making it easier to read:
Creator<T> result = new LazyLoader(...).Value; return result; // (is the syntax valid?)
What's the @ in @lock? - Anonymous
May 07, 2004
When you write a name in C#, you can optionally prefix it with @. If you do, you're telling the compiler that it's a name, not a keyword.
In this case, we wanted to call the lock "lock", but that's already a keyword in the language, so we escape it with @.
It's not a very good practice, IMO, but we liked the name. - Anonymous
May 07, 2004
Julien: Your confusion around the factory method is reasonable. We've got a method (Value) that looks a lot like a property. We use it without parens, so it looks even more like a method.
What we really want is to be able to use a property to satisfy a delegate, but the language doesn't allow that.
This concern has changed the latest implementation we came up with, which I'll post later. - Anonymous
May 07, 2004
Julien, we discussed removing the "new Creator<T>" from the method but decided against it because we thought the implicit conversion from method to delegate was very unclear.
When i instantiate a delegate i know that i must be passing it a method. However, if you just see:
Creator<T> result = new LazyLoader(...).Value;
return result;
or
return new LazyLoader(...).Value
I find it ambiguous and I end up asking myself: "Is Value a method? Is it a property that returns a Creator<T>?" - Anonymous
June 16, 2009
PingBack from http://fixmycrediteasily.info/story.php?id=4666