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.