New Reference type for use with the lazy loader
Theo and I were talking about places where you might use lazy loading and the singleton pattern. We were thinking about WeakReferences and how they were valuable but didn't allow you enough control over when the reference might be released. An example of where this would be important would be in some sort of server caching scenario. The first time someone hits a page you generate it and cache it. That way subsequent users will get the cached page. You want it to be weak so that the server can reclaim those resources if it needs to when people start hitting other pages. However, say the author of the page updates it. You don't want people to keep getting the cached version so you need a way to invalidate the reference. Of course, with our lazy loader we allowed you to provide your own IReference implementation so it would be very simple to add this behavior yourself. I struggled over the name of what such an object would be: InvalidatableReference? ResetableReference? It doesn't really roll of the tongue. (Names are very important to me and i spend a lot of time thinking about them because they tend to help you understand the system better). I went to talk to Damon (another guy here who comes in obscenely early like me) about it and he came up with “DisposableReference”. Sheer brilliance. By telling me that he connected the two concepts of reference and disposable things. I started to write it up and got this far:
namespace Testing.References
{
public class DisposableReference<A> : IReference<A>, IDisposable
{
IOptional<A> target = None<A>.Instance;
#region IReference<A> Members
public A Value
{
set
{
target = new Some<A>(value);
}
}
public IOptional<A> Target
{
get
{
return target;
}
}
#endregion
#region IDisposable Members
public void Dispose()
{
target = None<A>.Instance;
}
#endregion
}
}
But then i started to realize something. What if you wanted a WeakDisposableReference? You'd have to introduce another type into your system. Instead, it clicked that what we were doing was allowing a reference to return to it's original uninitialized state. So this was an operation that would be allowed on any reference. i.e.:
public interface IReference<A> : IDisposable
{
A Value { set; }
IOptional<A> Target { get; }
}
With the two implementations being:
public class StrongReference<A> : IReference<A>
{
IOptional<A> target = None<A>.Instance;
#region IOptional<A> Members
public IOptional<A> Target
{
get
{
return target;
}
}
public A Value
{
set
{
target = new Some<A>(value);
}
}
#endregion
#region IDisposable Members
public void Dispose()
{
target = None<A>.Instance;
}
#endregion
}
and
public class WeakReference<A> : IReference<A>
{
System.WeakReference reference = new System.WeakReference(null);
#region IOptional<A> Members
public IOptional<A> Target
{
get
{
IOptional<A> target = (IOptional<A>)reference.Target;
if (target == null)
{
return None<A>.Instance;
}
else
{
return target;
}
}
}
public A Value
{
set
{
reference = new System.WeakReference(new Some<A>(value));
}
}
#endregion
#region IDisposable Members
public void Dispose()
{
reference = new System.WeakReference(null);
}
#endregion
}
Now you can have all the benefits of lazy loading with weak/strong references, with the ability to invalidate the cached copy any time you want.
Comments
- Anonymous
May 21, 2004
Cool. It's fun to see that my instinct wasn't too far off, in our previous thread on Jay's blog :-P
Maybe there should be a pattern for resetting objects.
I still find it disturbing that StrongReference and WeakReference have to handle the "un-initizliazed" case differently (one with null, the other with None<A>.Instance). But I guess you don't have a choice because you depend on WeakReference.
BTW, a question I've always wondered about WeakReference: what is the "IsAlive" method for?
The reason I ask is because ".Target == null" would be equivalent and if you test with IsAlive before getting the value of Target you could get unlucky (if the GC runs between the two calls...).